Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save s1037989/86eaf9061211c0a53940b0d1e00c4e66 to your computer and use it in GitHub Desktop.

Select an option

Save s1037989/86eaf9061211c0a53940b0d1e00c4e66 to your computer and use it in GitHub Desktop.
perl pack / unpack
Perl’s pack and unpack templates define how binary data is converted to and from Perl scalars. Below are practical examples for the most commonly used templates.
Basic Integer Templates
Signed / Unsigned 8-bit
c — signed char
my $bin = pack('c', -5);
my $num = unpack('c', $bin);
print "$num\n"; # -5
C — unsigned char
my $bin = pack('C', 250);
my $num = unpack('C', $bin);
print "$num\n"; # 250
16-bit Integers
s / S — native endian short
my $bin = pack('S', 65535);
my $num = unpack('S', $bin);
print "$num\n";
Little endian
v — unsigned 16-bit little endian
my $bin = pack('v', 0x1234);
print unpack('H*', $bin), "\n";
# 3412
Big endian
n — unsigned 16-bit network order
my $bin = pack('n', 0x1234);
print unpack('H*', $bin), "\n";
# 1234
32-bit Integers
l / L — native long
my $bin = pack('L', 123456789);
my $num = unpack('L', $bin);
print "$num\n";
Little endian
V — unsigned 32-bit little endian
my $bin = pack('V', 0x12345678);
print unpack('H*', $bin), "\n";
# 78563412
Big endian
N — unsigned 32-bit network order
my $bin = pack('N', 0x12345678);
print unpack('H*', $bin), "\n";
# 12345678
64-bit Integers
q / Q — signed/unsigned quad
my $bin = pack('Q', 0x1122334455667788);
printf "%x\n", unpack('Q', $bin);
Floating Point
f — float
my $bin = pack('f', 3.14);
my $num = unpack('f', $bin);
print "$num\n";
d — double
my $bin = pack('d', 3.1415926535);
my $num = unpack('d', $bin);
print "$num\n";
Strings
a — null padded string
my $bin = pack('a10', 'abc');
print unpack('H*', $bin), "\n";
# 61626300000000000000
A — space padded string
my $bin = pack('A10', 'abc');
print unpack('H*', $bin), "\n";
# 61626320202020202020
Z — null terminated string
my $bin = pack('Z10', 'abc');
print unpack('H*', $bin), "\n";
# 61626300000000000000
Raw Binary / Hex
H — high nibble first hex
my $bin = pack('H*', 'deadbeef');
print unpack('H*', $bin), "\n";
# deadbeef
h — low nibble first hex
my $bin = pack('h*', '1234');
print unpack('H*', $bin), "\n";
Bit Strings
B — descending bit order
my $bin = pack('B8', '10101010');
print unpack('H*', $bin), "\n";
# aa
b — ascending bit order
my $bin = pack('b8', '10101010');
print unpack('H*', $bin), "\n";
# 55
Unicode
U — Unicode code point
my $bin = pack('U', 0x263A);
print $bin, "\n"; # ☺
Positioning / Padding
x — null byte padding
my $bin = pack('CxC', 1, 2);
print unpack('H*', $bin), "\n";
# 010002
@ — absolute position
my $bin = pack('C @4 C', 1, 2);
print unpack('H*', $bin), "\n";
# 0100000002
Repetition Counts
Fixed repeat count
my $bin = pack('C4', 1,2,3,4);
print unpack('H*', $bin), "\n";
# 01020304
* repeat all arguments
my $bin = pack('C*', 1..10);
my @nums = unpack('C*', $bin);
print "@nums\n";
Complex Structure Example
Suppose a binary structure:
Field Type
magic uint32 BE
version uint16 LE
flags uint8
name 8-byte string
my $bin = pack(
'N v C A8',
0xDEADBEEF,
2,
7,
'test'
);
my ($magic, $version, $flags, $name)
= unpack('N v C A8', $bin);
printf "magic=%08X\n", $magic;
print "version=$version\n";
print "flags=$flags\n";
print "name=$name\n";
Useful Debugging Trick
Hex dump packed data
print unpack('H*', $binary), "\n";
or formatted:
printf "%v02X\n", $binary;
Common Real-World Uses
Purpose Templates
TCP/IP headers n, N
Binary files C, v, V, a
ELF parsing C, v, V, Q
UUIDs H*
Bitfields B, b
Floating point protocols f, d
Fixed-width records A, a, Z
Full Reference Cheat Sheet
Template Meaning
c signed char
C unsigned char
s signed short
S unsigned short
l signed long
L unsigned long
q signed 64-bit
Q unsigned 64-bit
n uint16 BE
v uint16 LE
N uint32 BE
V uint32 LE
f float
d double
a null padded string
A space padded string
Z null terminated string
H hex high nibble first
h hex low nibble first
B bit string descending
b bit string ascending
U Unicode
x pad byte
@ absolute position
X move backward
* repeat all
Show how to use pack/unpack to convert 0x0a1b2c3d4e5f to 0x1b0a3d2c5f4e
This transformation swaps every adjacent pair of bytes:
0a 1b 2c 3d 4e 5f
1b 0a 3d 2c 5f 4e
You can do this cleanly with Perl pack and unpack.
use strict;
use warnings;
my $hex = '0a1b2c3d4e5f';
# Convert hex string to raw bytes
my $bin = pack('H*', $hex);
# Read bytes individually
my @bytes = unpack('C*', $bin);
# Swap adjacent bytes
for (my $i = 0; $i < @bytes; $i += 2) {
@bytes[$i, $i + 1] = @bytes[$i + 1, $i];
}
# Convert back to hex
my $out = unpack('H*', pack('C*', @bytes));
print "$out\n";
Output:
1b0a3d2c5f4e
Shorter Version
my $out = unpack(
'H*',
pack(
'C*',
map { @$_[1,0] }
unpack('(CC)*', pack('H*', '0a1b2c3d4e5f'))
)
);
print "$out\n";
Using Pure pack/unpack Templates
This version uses grouped unpacking:
my $hex = '0a1b2c3d4e5f';
my @pairs = unpack('(a2 a2)*', pack('H*', $hex));
my $swapped = join '', map { $_->[1] . $_->[0] } @pairs;
print unpack('H*', $swapped), "\n";
Most Efficient Binary-Oriented Version
For large data:
my $data = pack('H*', '0a1b2c3d4e5f');
$data =~ s/(.)(.)/$2$1/gs;
print unpack('H*', $data), "\n";
This regex version is typically the fastest and simplest for pairwise byte swapping.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment