如何在 perl 中完成这个字节修改?

How to accomplish this byte munging in perl?

背景:

我正在尝试使用 here 中的 perl 脚本来解密 android 备份。不幸的是,校验和验证失败。

玩完 this (Python) script 之后,问题似乎是我需要对主密钥进行一些额外的修改(n.b。[=64= 中的 masterKeyJavaConversion ] 脚本)。

问题:

我需要取一包字节并执行以下转换步骤:

例如(所有字节均为十六进制):

x始终保持不变。)

更具体地说,给定一个位序列1abc defg,我需要输出1110 1111 1011 111a 10bc defg。 (对于 0abc defg,输出只是 0abc defg,即不变。)


答案可以使用UTF转换,也可以直接进行位操作;我不在乎,只要它有效(这不是性能关键)。子程序形式的答案是理想的。 (我的主要问题是我对 Perl 的了解还不够危险。如果这是 C/C++,我不需要帮助,但是用另一种语言重写整个脚本将是一项重大任务,或者将 Python 脚本修改为不需要将整个输入读入内存。)

1110 1111 1011 111a 10bc defg 将是有效的 UTF-8 编码。

++++-------------------------- Start of three byte sequence
||||     ++------------------- Continuation byte
||||     ||       ++---------- Continuation byte
||||     ||       ||
11101111 1011111a 10bcdefg
    ||||   ||||||   ||||||
    ++++---++++++---++++++---- 1111 1111 1abc defg

这只是将 8 位有符号数扩展为 16 位,转换为无符号数,并被视为 Unicode 代码点。

所以,不看代码,我想你想要

sub encode_utf8 { 
   my ($s) = @_;
   utf8::encode($s);
   return $s;
}

sub munge {
   return
      encode_utf8                # "\x30\x70\xEF\xBE\xA0..."
         pack 'W*',              # "\x{0030}\x{0x0070}\x{0xFFA0}..."
            unpack 'S*',         # 0x0030, 0x0070, 0xFFA0, ...
               pack 's*',        # "\x30\x00\x70\x00\xA0\xFF..." (on a LE machine)
                  unpack 'c*',   # 48, 112, -96, ...
                     $_[0];      # "\x30\x70\xA0..."
}

my $s = "\x30\x70\xA0\xB0\xC0\xD0\xE0\xF0";
my $munged = munge($s);

如果删除评论,您会得到以下内容:

sub munge {
   my $s = pack 'W*', unpack 'S*', pack 's*', unpack 'c*', $_[0];
   utf8::encode($s);
   return $s;
}

这里有很多 faster 解决方案:

my @map = (
   ( map chr($_),            0x00..0x7F ),
   ( map "\xEF\xBE".chr($_), 0x80..0xBF ),
   ( map "\xEF\xBF".chr($_), 0xC0..0xFF ),
);

sub munge { join '', @map[ unpack 'C*', $_[0] ] }

这可能不如 ikegami's answer 优雅,但它确实有效:

sub munge_mk
{
  my $out;
  foreach(unpack('C*', $_[0])) {
    if($_ < 128) {
     $out .= chr($_);
    } else {
     my $hi = 0xbc | (($_ & 0xc0) >> 6);
     my $lo = 0x80 | ($_ & 0x3f);
     $out .= chr(0xef) . chr($hi) . chr($lo);
    }
  }
  return $out;
}