2

PNG、PHP、TGAファイルの3つのファイルを含むバイナリファイルがあります。

ここにあなたにアイデアを与えるファイルがあります:container.bin

ファイルは次のように作成されます。最初の6バイトはインデックスへのポインタであり、この場合は211794です。次に、3つのファイルすべてが次々にスタックされ、211794のオフセットで、ファイルの開始位置を示すインデックスが作成されます。そして終わり

この例では、次のようになります。

[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つのオフセット(ヘッダー)をチェックしてから、インデックスの場所にジャンプし、リストを使用してファイルを抽出する必要があります。

4

2 に答える 2

2

との組み合わせをseek使用readして、タスクを実行できます。

#!/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};
}
于 2012-11-18T08:59:03.503 に答える
2

ここで唯一難しいのは、入力ファイルと出力ファイルの両方がバイナリ モードで読み込まれるようにすることです。これは:raw、ファイルを開くときに PerlIO レイヤーを使用することで実現できます。

このプログラムは、あなたが望むことをするようです。最初にインデックス ブロックを検索して文字列に読み取り、次にその文字列を入力用に開き、各構成ファイルの開始位置と終了位置、および名前を読み取ります。その後の各ファイルの処理は簡単です。

ランダムなテキストにはスペースが含まれる可能性があるため、インデックス ブロックの書式設定があなたが言うよりも厳密でない限り、各行の最初、2 番目、および最後の空白で区切られたフィールドのみに頼ることができることに注意してください。また、スペースを含むファイル名を指定する方法もありません。

を使用した出力は、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;
}

出力

{
  "Capture.PNG"  => [6, 15149],
  "index.php"    => [15149, 15168],
  "untilted.tga" => [15168, 211794],
}
于 2012-11-18T12:02:39.260 に答える