Broadcom devices .trx firmware packaging: Perl parsing library

.trx file format is widely used for distribution of firmware updates for Broadcom devices. The most well-known are ASUS routers.

Fundamentally, it includes a footer which acts as a safeguard against installing a firmware package on a wrong hardware model or version, and a header which list numerous partitions packaged inside a single .trx file.

trx files not necessarily contain all these headers.

File extension

trx

KS implementation details

License: GPL-2.0-or-later
Minimal Kaitai Struct required: 0.9

This page hosts a formal specification of Broadcom devices .trx firmware packaging using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Perl source code to parse Broadcom devices .trx firmware packaging

BroadcomTrx.pm

# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use Encode;

########################################################################
package BroadcomTrx;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

}

sub header {
    my ($self) = @_;
    return $self->{header} if ($self->{header});
    my $_pos = $self->{_io}->pos();
    $self->{_io}->seek(0);
    $self->{header} = BroadcomTrx::Header->new($self->{_io}, $self, $self->{_root});
    $self->{_io}->seek($_pos);
    return $self->{header};
}

sub tail {
    my ($self) = @_;
    return $self->{tail} if ($self->{tail});
    my $_pos = $self->{_io}->pos();
    $self->{_io}->seek(($self->_io()->size() - 64));
    $self->{tail} = BroadcomTrx::Tail->new($self->{_io}, $self, $self->{_root});
    $self->{_io}->seek($_pos);
    return $self->{tail};
}

########################################################################
package BroadcomTrx::Revision;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{major} = $self->{_io}->read_u1();
    $self->{minor} = $self->{_io}->read_u1();
}

sub major {
    my ($self) = @_;
    return $self->{major};
}

sub minor {
    my ($self) = @_;
    return $self->{minor};
}

########################################################################
package BroadcomTrx::Version;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{major} = $self->{_io}->read_u1();
    $self->{minor} = $self->{_io}->read_u1();
    $self->{patch} = $self->{_io}->read_u1();
    $self->{tweak} = $self->{_io}->read_u1();
}

sub major {
    my ($self) = @_;
    return $self->{major};
}

sub minor {
    my ($self) = @_;
    return $self->{minor};
}

sub patch {
    my ($self) = @_;
    return $self->{patch};
}

sub tweak {
    my ($self) = @_;
    return $self->{tweak};
}

########################################################################
package BroadcomTrx::Tail;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{version} = BroadcomTrx::Version->new($self->{_io}, $self, $self->{_root});
    $self->{product_id} = Encode::decode("utf-8", IO::KaitaiStruct::Stream::bytes_terminate($self->{_io}->read_bytes(12), 0, 0));
    $self->{comp_hw} = ();
    my $n_comp_hw = 4;
    for (my $i = 0; $i < $n_comp_hw; $i++) {
        push @{$self->{comp_hw}}, BroadcomTrx::Tail::HwCompInfo->new($self->{_io}, $self, $self->{_root});
    }
    $self->{reserved} = $self->{_io}->read_bytes(32);
}

sub version {
    my ($self) = @_;
    return $self->{version};
}

sub product_id {
    my ($self) = @_;
    return $self->{product_id};
}

sub comp_hw {
    my ($self) = @_;
    return $self->{comp_hw};
}

sub reserved {
    my ($self) = @_;
    return $self->{reserved};
}

########################################################################
package BroadcomTrx::Tail::HwCompInfo;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{min} = BroadcomTrx::Revision->new($self->{_io}, $self, $self->{_root});
    $self->{max} = BroadcomTrx::Revision->new($self->{_io}, $self, $self->{_root});
}

sub min {
    my ($self) = @_;
    return $self->{min};
}

sub max {
    my ($self) = @_;
    return $self->{max};
}

########################################################################
package BroadcomTrx::Header;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{magic} = $self->{_io}->read_bytes(4);
    $self->{len} = $self->{_io}->read_u4le();
    $self->{crc32} = $self->{_io}->read_u4le();
    $self->{version} = $self->{_io}->read_u2le();
    $self->{flags} = BroadcomTrx::Header::Flags->new($self->{_io}, $self, $self->{_root});
    $self->{partitions} = ();
    do {
        $_ = BroadcomTrx::Header::Partition->new($self->{_io}, $self, $self->{_root});
        push @{$self->{partitions}}, $_;
    } until ( (($i >= 4) || (!($_->is_present()))) );
}

sub magic {
    my ($self) = @_;
    return $self->{magic};
}

sub len {
    my ($self) = @_;
    return $self->{len};
}

sub crc32 {
    my ($self) = @_;
    return $self->{crc32};
}

sub version {
    my ($self) = @_;
    return $self->{version};
}

sub flags {
    my ($self) = @_;
    return $self->{flags};
}

sub partitions {
    my ($self) = @_;
    return $self->{partitions};
}

########################################################################
package BroadcomTrx::Header::Partition;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{ofs_body} = $self->{_io}->read_u4le();
}

sub is_present {
    my ($self) = @_;
    return $self->{is_present} if ($self->{is_present});
    $self->{is_present} = $self->ofs_body() != 0;
    return $self->{is_present};
}

sub is_last {
    my ($self) = @_;
    return $self->{is_last} if ($self->{is_last});
    if ($self->is_present()) {
        $self->{is_last} =  (($self->idx() == (scalar(@{$self->_parent()->partitions()}) - 1)) || (!(@{$self->_parent()->partitions()}[($self->idx() + 1)]->is_present()))) ;
    }
    return $self->{is_last};
}

sub len_body {
    my ($self) = @_;
    return $self->{len_body} if ($self->{len_body});
    if ($self->is_present()) {
        $self->{len_body} = ($self->is_last() ? ($self->_root()->_io()->size() - $self->ofs_body()) : @{$self->_parent()->partitions()}[($self->idx() + 1)]->ofs_body());
    }
    return $self->{len_body};
}

sub body {
    my ($self) = @_;
    return $self->{body} if ($self->{body});
    if ($self->is_present()) {
        my $io = $self->_root()->_io();
        my $_pos = $io->pos();
        $io->seek($self->ofs_body());
        $self->{body} = $io->read_bytes($self->len_body());
        $io->seek($_pos);
    }
    return $self->{body};
}

sub ofs_body {
    my ($self) = @_;
    return $self->{ofs_body};
}

sub idx {
    my ($self) = @_;
    return $self->{idx};
}

########################################################################
package BroadcomTrx::Header::Flags;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{flags} = ();
    my $n_flags = 16;
    for (my $i = 0; $i < $n_flags; $i++) {
        push @{$self->{flags}}, $self->{_io}->read_bits_int_le(1);
    }
}

sub flags {
    my ($self) = @_;
    return $self->{flags};
}

1;