Perl-使用指针和索引进行二进制解包
我有一个包含3个文件的二进制文件,一个PNG、一个PHP和一个TGA文件 下面是一个文件,让您了解: 文件是通过以下方式生成的: 前6个字节是指向索引的指针,在本例中为211794 然后将所有3个文件依次堆叠 在211794的集合中,有一个索引,它告诉你文件的开始和结束位置 在本例中,您有:Perl-使用指针和索引进行二进制解包,perl,binary,block,Perl,Binary,Block,我有一个包含3个文件的二进制文件,一个PNG、一个PHP和一个TGA文件 下面是一个文件,让您了解: 文件是通过以下方式生成的: 前6个字节是指向索引的指针,在本例中为211794 然后将所有3个文件依次堆叠 在211794的集合中,有一个索引,它告诉你文件的开始和结束位置 在本例中,您有: [offset start] [offset end] [random data] [offset start] [name] 6 15149 asdf 6 Capture.PNG 15149 15168
[offset start] [offset end] [random data] [offset start] [name]
6 15149 asdf 6 Capture.PNG
15149 15168 4584 15149 index.php
15168 211794 12 15168 untilted.tga
这意味着capture.png在偏移量6处开始,在偏移量15149处结束,然后asdf是一个随机数据,并且再次重复开始偏移量
现在我要做的是用perl来分离这个二进制文件上的文件。
perl需要检查文件头的前6个偏移量,然后跳转到索引位置,并使用列表将文件提取出来。和的组合可用于完成任务:
#!/usr/bin/env perl
use strict;
use warnings;
use Fcntl 'SEEK_SET';
sub get_files_info {
my ( $fh, $offset ) = @_;
my %file;
while (<$fh>) {
chomp;
my $split_count = my ( $offset_start, $offset_end, $random_data, $offset_start_copy,
$file_name ) = split /\s/;
next if $split_count != 5;
if ( $offset_start != $offset_start_copy ) {
warn "Start of offset mismatch: $file_name\n";
next;
}
$file{$file_name} = {
'offset_start' => $offset_start,
'offset_end' => $offset_end,
'random_data' => $random_data,
};
}
return %file;
}
sub write_file {
my ( $fh, $file_name, $file_info ) = @_;
seek $fh, $file_info->{'offset_start'}, SEEK_SET;
read $fh, my $contents,
$file_info->{'offset_end'} - $file_info->{'offset_start'};
open my $fh_out, '>', $file_name or die 'Error opening file: $!';
binmode $fh_out;
print $fh_out $contents;
print "Wrote file: $file_name\n";
}
open my $fh, '<', 'container.bin' or die "Error opening file: $!";
binmode $fh;
read $fh, my $offset, 6;
seek $fh, $offset, SEEK_SET;
my %file = get_files_info $fh, $offset;
for my $file_name ( keys %file ) {
write_file $fh, $file_name, $file{$file_name};
}
和的组合可用于完成任务:
#!/usr/bin/env perl
use strict;
use warnings;
use Fcntl 'SEEK_SET';
sub get_files_info {
my ( $fh, $offset ) = @_;
my %file;
while (<$fh>) {
chomp;
my $split_count = my ( $offset_start, $offset_end, $random_data, $offset_start_copy,
$file_name ) = split /\s/;
next if $split_count != 5;
if ( $offset_start != $offset_start_copy ) {
warn "Start of offset mismatch: $file_name\n";
next;
}
$file{$file_name} = {
'offset_start' => $offset_start,
'offset_end' => $offset_end,
'random_data' => $random_data,
};
}
return %file;
}
sub write_file {
my ( $fh, $file_name, $file_info ) = @_;
seek $fh, $file_info->{'offset_start'}, SEEK_SET;
read $fh, my $contents,
$file_info->{'offset_end'} - $file_info->{'offset_start'};
open my $fh_out, '>', $file_name or die 'Error opening file: $!';
binmode $fh_out;
print $fh_out $contents;
print "Wrote file: $file_name\n";
}
open my $fh, '<', 'container.bin' or die "Error opening file: $!";
binmode $fh;
read $fh, my $offset, 6;
seek $fh, $offset, SEEK_SET;
my %file = get_files_info $fh, $offset;
for my $file_name ( keys %file ) {
write_file $fh, $file_name, $file{$file_name};
}
这里唯一真正的困难是确保以二进制模式读取输入和输出文件。这可以通过在打开文件时使用:raw PerlIO层来实现 这个程序似乎能满足你的需要。它首先定位索引块并将其读入字符串,然后打开该字符串进行输入,并读取每个组成文件的开始和结束位置以及名称。此后,处理每个文件都很简单 请注意,除非索引块的格式比您所说的更严格,否则您只能依赖每行上第一个、第二个和最后一个空格分隔的字段,因为随机文本可能包含空格。也无法指定包含空格的文件名 使用Data::Dump的输出用于演示正确的功能,而不是程序运行所必需的
use v5.10;
use warnings;
use Fcntl ':seek';
use autodie qw/ open read seek close /;
open my $fh, '<:raw', 'container.bin';
read $fh, my $index_loc, 6;
seek $fh, $index_loc, SEEK_SET;
read $fh, my ($index), 1024;
my %contents;
open my $idx, '<', \$index;
while (<$idx>) {
my @fields = split;
next unless @fields;
$contents{$fields[-1]} = [ $fields[0], $fields[1] ];
}
use Data::Dump;
dd \%contents;
for my $file (keys %contents) {
my ($start, $end) = @{ $contents{$file} };
my $size = $end - $start;
seek $fh, $start, SEEK_SET;
my $nbytes = read $fh, my ($data), $size;
die "Premature EOF" unless $nbytes == $size;
open my $out, '>:raw', $file;
print { $out } $data;
close $out;
}
这里唯一真正的困难是确保以二进制模式读取输入和输出文件。这可以通过在打开文件时使用:raw PerlIO层来实现 这个程序似乎能满足你的需要。它首先定位索引块并将其读入字符串,然后打开该字符串进行输入,并读取每个组成文件的开始和结束位置以及名称。此后,处理每个文件都很简单 请注意,除非索引块的格式比您所说的更严格,否则您只能依赖每行上第一个、第二个和最后一个空格分隔的字段,因为随机文本可能包含空格。也无法指定包含空格的文件名 使用Data::Dump的输出用于演示正确的功能,而不是程序运行所必需的
use v5.10;
use warnings;
use Fcntl ':seek';
use autodie qw/ open read seek close /;
open my $fh, '<:raw', 'container.bin';
read $fh, my $index_loc, 6;
seek $fh, $index_loc, SEEK_SET;
read $fh, my ($index), 1024;
my %contents;
open my $idx, '<', \$index;
while (<$idx>) {
my @fields = split;
next unless @fields;
$contents{$fields[-1]} = [ $fields[0], $fields[1] ];
}
use Data::Dump;
dd \%contents;
for my $file (keys %contents) {
my ($start, $end) = @{ $contents{$file} };
my $size = $end - $start;
seek $fh, $start, SEEK_SET;
my $nbytes = read $fh, my ($data), $size;
die "Premature EOF" unless $nbytes == $size;
open my $out, '>:raw', $file;
print { $out } $data;
close $out;
}
听起来你想看看解包函数。运行perldoc-f unpack,尽管perldoc-f pack具有指定解包模板的所有值。问题是什么?哪个部分有问题?实际上,解包在这里没有用,因为211794存储为ASCII,而不是int。只需一些查找和读取就可以了。回避了一个问题,如果偏移量恰好是大于999999的数字怎么办?听起来你想看看解包函数。运行perldoc-f unpack,尽管perldoc-f pack具有指定解包模板的所有值。问题是什么?哪个部分有问题?实际上,解包在这里没有用,因为211794存储为ASCII,而不是int。只需进行一些查找和读取即可。回避了一个问题,如果偏移量恰好大于99999,该怎么办?这在Unix系统上可以正常工作,但是区分文本文件和二进制文件的平台需要调用binmode来正确读取和写入二进制文件。@Borodin,即使在unix平台上,如果默认情况下设置为完成解码,也需要调用binmode。我已将其添加到代码中。这在Unix系统上可以正常工作,但区分文本文件和二进制文件的平台需要调用binmode来正确读取和写入二进制文件。@Borodin,即使在Unix平台上,如果默认情况下设置为完成解码,也需要它。我已将其添加到代码中。