如何 enter/answer 来自 Perl 脚本的终端提示符?

How to enter/answer a terminal prompt from a Perl script?

我正在尝试破解一个被遗忘的 luks 分区密码。我生成了一个组合列表,现在我正在尝试从 Perl 脚本解密该卷。

问题是从脚本本身输入提示,因为:system('cryptsetup', ('open', '/dev/sdd1', 'crypt-vol', '--type=luks')) 只是吐出 Enter passphrase for /dev/sdd1 并等待我手动输入。

我该如何解决这个问题?

非常感谢您的帮助。

* 这是我的音量,我还没有完全忘记密码,所以我创建了组合列表,前提是我记得一些细节。好像>6k的可能性,所以打破它应该是可行的。

不要,使用带有 cryptsetup 的 'keyfile'。密钥文件可以是 STDIN。

所以:

echo "passphrase_here" | cryptsetup -d - <other  options>

在 perl 中,您可以为此使用 IPC::Run2,这允许您 read/write 到 FH,但如果您只需要一个 return 代码来测试密码,则不需要.

例如https://blog.sleeplessbeastie.eu/2019/03/27/how-to-test-luks-passphrase/

所以:

open ( my $crypter, '|-', "cryptsetup luksOpen -d - --test-passphrase " )

print {$crypter} "Your passphrase";

close ( $crypter );
print "Got return code of $?"

我真的很喜欢使用 STDIN 的想法,所以如果有人看到此页面,请考虑是否可以在您的情况下使用 STDIN。

但我想 post 我自己的答案,它使用 Expect Perl 模块,因为:

  • 我用过它,它被证明至少在 Linux 上有效(尽管评论中的问题提到该模块在 Windows 上对 运行 有问题);
  • 由于问题的标题没有提到cryptsetup(可以接受STDIN上的密码),所以Expect模块在这里就像一个通用的解决方案,应该适用于其他工具;

我的解决方案如下。我没有读太多文档,所以可能有更好的方法来做到这一点。对我来说,掌握 object 实例化和 expect/send 方法的思想就足够了。

您创建一个实例,就好像您刚刚在终端中启动了一个程序:

my $exp = Expect->new('progName', ($arg1, $arg2, $etc));

然后可以通过 expect(waits/confirms 程序向用户输出)和 send(允许用户 'type' ) 方法。

expect 可以在标量上下文或列表上下文中调用(我使用列表上下文),它接受一个字符串或正则表达式来表示期望的输出和持续时间。如果在指定时间内没有出现预期的输出,则会抛出错误:

                       #sec to wait for  #use regexp (don't match exactly) #regexp to match against
my @out = $exp->expect(10,               '-re',                            'smth');

send 只接受输入

$exp->send('some chars');

就是这样,只需要创建一个脚本,就像人类用户一样工作。


为了以防万一它可能对某些人来说很方便,我将 post 我完整的 cryptsetup 特定解决方案(我已经在虚拟卷上对其进行了测试,它能够挂载,并且我已经 运行 在真实卷上尝试了 6k 种组合,没有任何明显问题):

*我忘了在这里关闭文件描述符,所以应该在适当的地方添加close($fd)

use strict;
use warnings;
use feature 'say';


# I installed Expect with its deps locally in my situation, so I had to change @INC
BEGIN {
  unshift(@INC, './perl-mods/lib/perl5/');
  unshift(@INC, './perl-mods/lib/perl5/x86_64-linux-thread-multi/');
}

use Expect;

my $devName = '/dev/sdb3';
my $combinationsFileName = 'combs.txt';
open(my $fd, '<', $combinationsFileName);
my $lineCount = 0;

while (<$fd>) {
  my $pass = $_;
  chomp($_);
  say $_ . ' ' . ++$lineCount;

  my $exp = Expect->new('cryptsetup', ('open', $devName, 'crypt-vol')) or die 'err 1';
  my @out = $exp->expect(10, '-re', 'Enter passphrase') or die 'err 2';
  $exp->send($pass);
  @out = $exp->expect(10, '-re', 'No key available') or die 'err 3';

  #if cryptsetup returned an error code
  if ($out[1]) {
    die $out[1] . ' ' . $pass;
  }
}