파티션 테이블로 LUKS 덮어쓰기

파티션 테이블로 LUKS 덮어쓰기

이제 이것이 어리석은 결정이었다는 것을 알고 있습니다. Windows 설치 프로그램을 사용하여 Windows 및 Linux 이중 부팅을 시도했습니다. Windows 설치 프로그램을 시작한 후 약 500GB 크기의 복제된 하드 드라이브 2개 중 하나를 선택하여 삭제했습니다. 내가 다른 것보다 하나를 선택하는 것은 중요하지 않습니다.

이 작업을 수행한 후 설치 프로그램은 500GB 하드 드라이브 중 하나의 파티션 테이블을 변경한 다음 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 헤더를 복구하려면 최소한 두 가지가 필요합니다. 하나는 최소한 하나의 키홈에 대한 키 자료입니다. 둘째, 핵심 자료가 사용되는 방식(알고리즘, 반복 횟수, 솔트 등)을 설명하는 메타데이터입니다.

키 자료는 약 256KB의 무작위 데이터로, 일반적으로 오프셋 32768(0x8000) 이상에서 발견되지만 정확한 오프셋과 크기는 메타데이터에서 결정해야 합니다.

메타데이터는 일반적으로 오프셋 4096(0x1000) 및 20480(0x5000)에 있는 JSON 문자열입니다. LUKS2는 두 개의 동일한 복사본(기본 헤더와 보조 헤더)을 유지합니다. 키 자료 자체는 한 번만 존재합니다.

파티션 테이블 자체가 손실된 경우 올바른 파티션 오프셋도 결정해야 합니다.


설정:

# 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에는 기본 헤더와 보조 헤더 모두에 대한 체크섬이 있습니다. 체크섬을 업데이트하지 않고 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"

완벽한.마침내.

관련 정보