パーティションテーブルでLUKSを上書きする

パーティションテーブルでLUKSを上書きする

今これは愚かな決定だったことを知っています。 Windowsインストーラを使用してWindowsとLinuxのデュアルブートを試みました。 Windowsインストーラを起動した後、約500 GBのサイズの2つのクローンハードドライブを選択して削除しました。私が他のものより1つを選択することは重要ではありません。

これを行った後、インストーラは500 GBのハードドライブの1つのパーティションテーブルを変更してからWindowsをインストールできませんでした。ファイルをコピーせずにエラーが原因で競合が発生したり、インストールを開始したりできないというメッセージが表示された場合は、信頼できるかどうかはわかりません。

そのため、Linuxインストールを起動して、どのドライブに適用されたかを確認し、手動でインストールしました。代わりに、他のドライブである6TBのdm-luksドライブとbtrfsドライブがないことがわかりました。 500GBドライブはすべて影響を受けなかっただけでなく、6TBドライブには混乱したパーティションがたくさん追加されているようです。 499M、99M、499M、100M、499M、100Mの順序で6つのパーティション。

私のドライブは大きくて遅いので、hexdump -C /dev/sda |grep LUKSこれまで実行結果が多すぎて完了したら更新します。

8d411ce0  e1 ad 4c 55 4b 53 c0 85  22 3d de 49 dd 44 fd 08  |..LUKS.."=.I.D..|
e6449610  d5 cf 4a 86 9f cc 4c 55  4b 53 a9 a9 16 cc ba 1d  |..J...LUKS......|
446ea9a70  b3 db a9 bf 8b 2e 41 4c  55 4b 53 ef f0 75 b0 18  |......ALUKS..u..|
4732c6040  e0 b3 bb ff 4c 55 4b 53  4c c2 5b 12 c6 41 fc d6  |....LUKSL.[..A..|

これが発生して以来、これまでディスクにヒットした唯一のことはhexdumpであり、testdiskがドライブのデータを上書きしてluksを検索できる項目としてリストしないと聞いたため、実行することが躊躇しました。

他の人が hexdump を使用してヘッダー全体をチェックするのを見ることができますが、正確に私が探しているものが何であるかはわかりません。

この時点でヘッダーの一部を回復できることを確認するために何ができますか? testdiskまたは他のツールを実行してluksヘッダーを見つけて上書きしたかどうかを確認する方法はありますか?すべてがFUBARであることを知らせる方法は、データを回復する方法と同じくらい歓迎されています。

編集する

grepなしでドライブの最初のビットでhexdumpを実行すると、少なくともいくつかの完全なJSONが表示されます。からまでの00005000内容00005310は、私が今具体的に探していることがまだ完全であるかどうかはわかりません。正確にこの文字列までのデータを上書きするようです。

00005000  7b 22 6b 65 79 73 6c 6f  74 73 22 3a 7b 22 30 22  |{"keyslots":{"0"|
00005010  3a 7b 22 74 79 70 65 22  3a 22 6c 75 6b 73 32 22  |:{"type":"luks2"|

ソルトが含まれているため、途中のデータは削除されますが、ブロックは次に終了します。

000052f0  22 2c 22 6b 65 79 73 6c  6f 74 73 5f 73 69 7a 65  |","keyslots_size|
00005300  22 3a 22 31 36 37 34 34  34 34 38 22 7d 7d 00 00  |":"16744448"}}..|
00005310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

それは損なわれていません十分

答え1

cryptsetup repair、パート2 - フルヘッダー回復

部分的に上書きされたLUKS2ヘッダーを回復するには、少なくとも2つが必要です。 1つは少なくとも1つのキーホームのキーデータです。第二に、コアデータの使用方法(アルゴリズム、繰り返し数、ソルトなど)を説明するメタデータです。

キーデータは約256KBのランダムデータで、通常はオフセット32768(0x8000)以上で検出されますが、正確なオフセットとサイズはメタデータで決定する必要があります。

メタデータは通常、オフセット4096(0x1000)および20480(0x5000)にあるJSON文字列です。 LUKS2は、2つの同じコピー(基本ヘッダーと補助ヘッダー)を保持します。鍵素材自体は一度だけ存在します。

パーティションテーブル自体が失われた場合は、正しいパーティションオフセットも決定する必要があります。


設定:

# truncate -s 128M disk.img
# losetup --find --show --partscan disk.img
/dev/loop0
# parted /dev/loop0 -- mklabel gpt
# parted /dev/loop0 -- mkpart luks $((RANDOM%100))MiB 100%
# cryptsetup luksFormat --type luks2 /dev/loop0p1

WARNING!
========
This will overwrite data on /dev/loop0p1 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/loop0p1:
Verify passphrase:
# cryptsetup open /dev/loop0p1 luks
Enter passphrase for /dev/loop0p1:
# mkfs.ext2 -L encrypted /dev/mapper/luks
# blkid /dev/mapper/luks
/dev/mapper/luks: LABEL="encrypted" […] TYPE="ext2"

輝く新しい暗号化ファイルシステム!

損傷:

# cryptsetup close luks
# wipefs -a /dev/loop0p1
/dev/loop0p1: 6 bytes were erased at offset 0x00000000 (crypto_LUKS): 4c 55 4b 53 ba be
/dev/loop0p1: 6 bytes were erased at offset 0x00004000 (crypto_LUKS): 53 4b 55 4c ba be
# dd count=32 if=/dev/urandom of=/dev/loop0p1
32+0 records in
32+0 records out
16384 bytes (16 kB, 16 KiB) copied, 0.000334077 s, 49.0 MB/s
# wipefs -a /dev/loop0
/dev/loop0: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/loop0: 8 bytes were erased at offset 0x063ffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/loop0: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
/dev/loop0: calling ioctl to re-read partition table: Success
# losetup -d /dev/loop0

したがって、これは不明なオフセットにLUKS2パーティションを持つdisk.imgであり、そのヘッダーが破損しています(マジックバイトが消去され、部分的に上書きされ、パーティションテーブルが消去されます)。


メタデータの回復:

LUKS2 JSON文字列は純粋なASCIIなので、を使用して見つけることができ、stringsオフセットも表示されます。

# stdbuf -oL strings -n 64 -t d disk.img | grep '"keyslots":'
60837888 {"keyslots":{"0":{"type":"luks2","key_size":64,"af":{"type":"luks1","stripes":4000,"hash":"sha256"},"area":{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64},"kdf":{"type":"argon2id","time":13,"memory":1048576,"cpus":4,"salt":"R1z3arzSCjRb3STaCAnstIygkHCXf0CHf6kXl5yQj/E="}}},"tokens":{},"segments":{"0":{"type":"crypt","offset":"16777216","size":"dynamic","iv_tweak":"0","encryption":"aes-xts-plain64","sector_size":512}},"digests":{"0":{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"hash":"sha256","iterations":324435,"salt":"0nSkpvmDJlvfkDaQteVVo6JdD/Oqt3vnndkZt1Qnd84=","digest":"lefQ21EaiuSdHFhSIFW3wDfMcRqG0HLCAO1bGI3SfvM="}},"config":{"json_size":"12288","keyslots_size":"16744448"}}

ここでは、オフセット60837888に完全なJSON文字列があります。コピーしてファイルに貼り付けますheader.json。ファイルは{で始まり、終了する必要があります}。これを使用して、jq実際に有効なJSON文字列であることを確認し、人間がより読みやすい形式で表示できます。

# jq < header.json
{
  "keyslots": {
    "0": {
      "type": "luks2",
[…]
  }
}

パーティションの回復:

LUKS2 ヘッダー内の JSON メタデータのオフセットは、基本ヘッダーか補助ヘッダーかによって、通常 4096 または 20480 です。strings以前に見つけたオフセットからこの値を減算する必要があります。

したがって、この場合、正しいパーティションオフセットは、またはになる可能性が60837888 - 4096 = 60833792 = 58.02MiBあります60837888 - 20480 = 60817408 = 58 MiB。後者はMiBでソートされているため、正しいパーティションオフセットの候補となる可能性が高くなります。

疑わしい場合は、両方を試してください。


重要物質のリサイクル:

JSONメタデータによると、このLUKS2ヘッダーにはキーデータを見つけるためのキースロットがあります"offset":"32768","size":"258048"。それをつかみましょうdd

# partition=60817408
# offset=32768
# size=258048
# dd bs=1 skip=$((partition+offset)) count=$((size)) if=disk.img of=header.$((offset))

複数のキーホームがある場合は、各キーホームに対してこの手順を繰り返します。

キーマテリアルは、任意のデータのように見えるはずです。これを確認するには、を使用して内容全体を表示できますhexdump -C

# hexdump -C header.32768
00000000  f1 3b 23 73 98 d7 8f e3  22 24 9a 9d 5a 2c a9 ae  |.;#s...."$..Z,..|
00000010  95 82 3e c6 df e7 0e a0  f4 ba 54 6c 7f e9 fa f6  |..>.......Tl....|
00000020  b7 12 64 8d 7d a5 ca 4b  c8 89 89 08 3e de 59 0d  |..d.}..K....>.Y.|
[…]
0003efe0  b2 b3 bc cd de 60 17 a7  57 bb 1a 84 5a 15 68 95  |.....`..W...Z.h.|
0003eff0  7f 1f 07 ee ee d1 e8 a2  6c cf 5f 40 0b 73 00 0b  |[email protected]..|
0003f000

あるいは、圧縮を試みて圧縮結果が小さいことを確認することもできます。

# gzip < header.32768 > header.32768.gz
# stat -c %s header.*
258048
258106

ランダムデータは一般的にまったく圧縮できないため、gzip圧縮バージョンが小さくない(または数バイト以上)、データ全体がランダムデータである可能性が高くなります。

実際の確認は、パスワードを受け入れたり受け入れたりしない最後の段階でのみ可能です。


完全なヘッダー回復:

上記で必要なコンポーネントを収集したら、ヘッダー全体を再構成してみることができます。

# truncate -s 16M luks.recovery
# cryptsetup luksFormat --type luks2 luks.recovery
# cryptsetup luksErase luks.recovery

cryptsetupを使用して有効であるが使用できないヘッダー(キースロットなし)を生成します。ここでの目標は、マジックバイト、UUIDなどのすべての正しい設定を含むファイルを取得することです。暗号化とは関係ありませんが、LUKSヘッダーをLUKSヘッダーにすることです。

ここでメタデータをここに移植しましょう。

# printf "%s\0" "$(jq -c < header.json)" |
    dd conv=notrunc bs=1 seek=4096 of=luks.recovery
# printf "%s\0" "$(jq -c < header.json)" |
    dd conv=notrunc bs=1 seek=20480 of=luks.recovery

主な材料:

# dd conv=notrunc bs=1 seek=32768 if=header.32768 of=luks.recovery

この時、私たちはついに以下を除いて完了してください。

# cryptsetup luksDump luks.recovery
Device luks.recovery is not a valid LUKS device.
# cryptsetup repair luks.recovery
Device luks.recovery is not a valid LUKS device.

チェックサムの回復:

ええと、今どうしたの?--debug学習するには追加します。

# cryptsetup luksDump --debug luks.recovery
[…]
# LUKS2 header version 2 of size 16384 bytes, checksum sha256.
# Checksum:5babf58f0f788911897989ff3d9a580de1c22db8869b3b08cd0d6d56906005cb (on-disk)
# Checksum:b2ff5dd7b53978723402103ba914ed87ef2c5b5d9a9062d68363e4df38aebf6f (in-memory)
[…]

LUKS2 には、1 次ヘッダーと 2 次ヘッダーの両方のチェックサムがあります。チェックサムを更新せずにJSONメタデータを変更したため、これは矛盾です。幸いなことに、cryptsetupは期待値を表示するので、手動で計算する必要はありません。

このチェックサムはバイナリヘッダーの一部であるため、xxd -r -p次のようにバイナリに変換する必要があります。

# echo 5babf58f0f788911897989ff3d9a580de1c22db8869b3b08cd0d6d56906005cb | xxd -r -p | hexdump -C
00000000  5b ab f5 8f 0f 78 89 11  89 79 89 ff 3d 9a 58 0d  |[....x...y..=.X.|
00000010  e1 c2 2d b8 86 9b 3b 08  cd 0d 6d 56 90 60 05 cb  |..-...;...mV.`..|
00000020
# echo b2ff5dd7b53978723402103ba914ed87ef2c5b5d9a9062d68363e4df38aebf6f | xxd -r -p | hexdump -C
00000000  b2 ff 5d d7 b5 39 78 72  34 02 10 3b a9 14 ed 87  |..]..9xr4..;....|
00000010  ef 2c 5b 5d 9a 90 62 d6  83 63 e4 df 38 ae bf 6f  |.,[]..b..c..8..o|
00000020

無効なディスクチェックサムを正しいメモリチェックサムに置き換えます。

# hexdump -C luks.recovery | grep '5b ab f5 8f 0f 78 89 11'
000001c0  5b ab f5 8f 0f 78 89 11  89 79 89 ff 3d 9a 58 0d  |[....x...y..=.X.|
# echo b2ff5dd7b53978723402103ba914ed87ef2c5b5d9a9062d68363e4df38aebf6f |
    xxd -r -p |
    dd conv=notrunc bs=1 seek=$((0x000001c0)) of=luks.recovery

これにより、仕事が進みます。

# cryptsetup repair luks.recovery
# cryptsetup luksDump luks.recovery
LUKS header information
Version:        2
[…]

結論として:

# losetup --find --show --read-only --offset 60817408 disk.img
/dev/loop0

# cryptsetup open --read-only --header luks.recovery /dev/loop0 luksrecovery
Enter passphrase for /dev/loop0:

# blkid /dev/mapper/luksrecovery
/dev/mapper/luksrecovery: LABEL="encrypted" […] TYPE="ext2"

完璧。ついに

関連情報