【问题标题】:Edit bytes at particular offsets in stream编辑流中特定偏移量的字节
【发布时间】:2014-11-15 06:02:05
【问题描述】:

在 awk 程序中,我调用了一些生成一组二进制字符串的命令,它们都一起运行。

在每个字符串内:

  • 偏移量 0x04 处的字节是类型代码
  • 从偏移量 0x09 开始的四个字节是字符串的(小端序)长度。

如果类型码是0x20,我想改成0x1E。

例子:

第一个字符串的前几个字节是:

4a d8 64 54 13 01 00 00 00 2d 00 00 00

从偏移量 0x04,我们看到类型代码是 0x13,所以什么都不做。

从偏移量 0x09 开始,我们看到长度是 2d 00 00 00。从这个字符串的开头推进那么远,找到下一个字符串的开头。

下一个字符串开始:

4a d8 64 54 20 01 00 00 00 2c 00 00 00

从偏移量 0x04,我们看到类型代码是 0x20。将其更改为 0x1E。

从偏移量 0x09 开始,我们看到长度为 2c 00 00 00。从该字符串的开头向前推进,以找到下一个字符串(或 EOF)的开头。

什么工具最适合这个?

【问题讨论】:

  • 我认为这不是awk 的任务。 perl 更适合于此(请参阅:pack()unpack())。
  • 您能否解析数据,使其显示为您的示例?然后你可以使用if ($4 == "20") ... 类型测试。 ?但是,perl 似乎在需要不可避免的改进时会更好,如果你能负担得起学习曲线的话。祝你好运。
  • 我有点困惑。如果偏移量是 little endian,那么如果字节按照 '2d' '00' '00' '00' 指向更高的内存地址,那么它的值是 0x2d,而不是 0x2d000000。

标签: bash perl awk


【解决方案1】:

我在 中实现了一个解决方案。我假设大小不包括 13 字节的标头。它还没有完全成熟,但可以工作。它是一种工作台模型。它在内存中创建一些数据,然后进行修改。

所以第一部分只是根据标题创建内存中的数据:

#!/usr/bin/perl

use strict;
use warnings;

# Glues binary strings -> not needed $a.$b works properly
#sub glue(@) { pack "W*", map { unpack("W*", $_) } @_; }

# Creates a <header:null data> record
sub pck(@) {
  my $h = pack "(H[2])*", @_;
  # If it is big-endian use N instead of V!!!
  # It has to be unpacked to get the 32 bit data size
  my @u = unpack "C5V2", $h;
  my $b = pack "x$u[6]"; # put a lot of NULL bytes

  $h.$b;
}

my @h1=qw(4a d8 64 54 13 01 00 00 00 2d 00 00 00);
my @h2=qw(4a d8 64 54 20 01 00 00 00 2c 00 00 00);
my $d1 = pck @h1;
my $d2 = pck @h2;
my $d = $d1.$d2;

现在$d 包含两条记录:&lt;header1&gt;&lt;zero data1&gt;&lt;header2&gt;&lt;zero data2&gt;。现在我们可以转储数据了:

sub dumpbin(@) {
  open my $fh, "|od -t x1" or die;
  binmode $fh;
  print $fh @_;
  close $fh;
}

dumpbin($d);

输出:

0000000 4a d8 64 54 13 01 00 00 00 2d 00 00 00 00 00 00
0000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000060 00 00 00 00 00 00 00 00 00 00 4a d8 64 54 20 01
0000100 00 00 00 2c 00 00 00 00 00 00 00 00 00 00 00 00
0000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000160 00 00 00
0000163

现在可以根据需要解析和修改内存中的数据。标头被转储。偏移量以八进制打印以符合od 的输出。

print "=====\n";
for (my $offs=0; $offs<length($d);) {
  # Unpack header
  my @h = unpack "C5V2", substr($d, $offs, 13);
  # Checks if the header type is 0x20 and modifies it
  if ($h[4] eq 0x20) { substr($d, $offs + 4, 1) = pack "W", 0x1e;}
  printf "%07o HDR: %s%s\n", $offs, join(" ", map {sprintf "%x", $_} @h),
    $h[4] eq 0x20 ? " *" : "";
  $offs += 13 + $h[6];
}
print "=====\n";

输出:(用'*'标记的修改行)

=====
0000000 HDR: 4a d8 64 54 13 1 2d
0000072 HDR: 4a d8 64 54 20 1 2c *
=====

如果我们转储结果,可以看出数据已正确更改:

dumpbin($d);

输出:

0000000 4a d8 64 54 13 01 00 00 00 2d 00 00 00 00 00 00
0000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000060 00 00 00 00 00 00 00 00 00 00 4a d8 64 54 1e 01
0000100 00 00 00 2c 00 00 00 00 00 00 00 00 00 00 00 00
0000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0000160 00 00 00
0000163

可以看出数据被修改了。

如果输入文件很大,则需要更多的代码来分块读取数据,修改和写回。

我希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多