Skip to content

Latest commit

 

History

History
210 lines (162 loc) · 8.98 KB

阵列恢复大师.md

File metadata and controls

210 lines (162 loc) · 8.98 KB

阵列恢复大师

这题我做法比较笨,完全是人工看文件片段拼图拼出来的。拼图过程中逐步推断出 stripe size 和盘的顺序。

1 - RAID 0

string, xxd 先扫一下文件内容。盘里有一些小段文本文件,以及三个 PDF 文件。PDF 文件开头和结尾有明确的 magic string(%PDF%%EOF),然后文件中有一些部分(可能类似 PostScript 中的 command)是 ASCII 文本,特征很明显。

观察几个短的文本文件可以推断出文件系统 block size 是 4096 byte。比如看 1GHGGrmaMM0.img00c09000-00c09fff,最开始是个 Dockerfile,后面补 0 填满 4096 byte,然后开始下一个文件。不过这个信息没啥直接用途,只是帮助理解一下文件存储方式。

注意力还是回到那三个 PDF 文件。这里列出镜像里的三组 PDF 文件头和结尾的位置:

  • 文件开头 %PDF
    • BOF1: ID7sM2RWkyI.img:008ae000
    • BOF2: jCC60mutgoE.img:008c4000
    • BOF3: RApjvIxRlu0.img:008d2000
  • 文件结尾 %%EOF
    • EOF1: jCC60mutgoE.img:008c36ad
    • EOF2: RApjvIxRlu0.img:008d1d35
    • EOF3: wlOUASom2fI.img:008e426d

这几个文件看起来挺大,显然超出了一个 fs block。试着用文件内容不连续的位置来判断 stripe size。ID7sM2RWkyI.img:008ae000 这里开始有个 PDF 文件,然后一直到 008dffff 断了,没有 EOF,后面开始是 0 字节。可以推断 008e0000 应该是新 stripe 开始的位置:

008ae000: 2550 4446 2d31 2e33 0a25 c4e5 f2e5 eba7  %PDF-1.3.%......
......
008dfff0: 46a1 4f02 d9fe 5c69 e7df ec6f e647 95a8  F.O...\i...o.G..
008e0000: 0000 0000 0000 0000 0000 0000 0000 0000  ................

另一方面,jCC60mutgoE.img:008c0000 突然从字节0变成疑似 PDF 中间部分的内容,这里应该也是新 stripe 的起点:

008bfff0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
008c0000: 3030 3020 6e20 0a30 3030 3030 3736 3336  000 n .000007636

以上是两个例子,实际上每个位置我都看了好几个文件相互验证。总之最后得到这两个 stripe 起点位置,相减得到 stripe size 最多是 128K,也可能是 64K、32K…… 然后,依然通过文件连续性来排除其他可能。比如 1GHGGrmaMM0.img:008d0000 这里文件是连续的,就可以排除 64K:

008cfff0: 2031 3239 3420 3020 5220 3132 3935 2030   1294 0 R 1295 0
008d0000: 2052 0a31 3239 3620 3020 5220 3132 3937   R.1296 0 R 1297

类似方法排除其他 stripe size,最终确定 RAID stripe size 是128K。

接下来是判断盘的顺序。观察每个盘的 stripe #70(008c0000~008dffff) 的开始和末尾的内容:

for f in *.img; do echo $f; xxd -s 0x008c0000 -l 131072 $f | sed -e 1b -e '$!d'; done
1GHGGrmaMM0.img
008c0000: 324a b3e3 6006 edee 4844 8ee8 e8dc 7f6b  2J..`...HD.....k
008dfff0: 5375 6274 7970 6520 2f4c 696e 6b20 2f52  Subtype /Link /R
5qiSQnlrA4Y.img
008c0000: 6563 7420 5b31 3138 2e33 3632 3520 3233  ect [118.3625 23
008dfff0: 675f 7379 7374 656d 5f72 6573 706f 6e73  g_system_respons
d3Be7V0EVKo.img
008c0000: 6976 656e 6573 735f 756e 6465 725f 6c6f  iveness_under_lo
008dfff0: b2e8 f53a cec3 c6d8 b2d6 62e5 3cd6 644c  ...:......b.<.dL
eRL2MQSdOjo.img
008c0000: b171 2c10 5b04 51ab b342 ce16 b301 59a6  .q,.[.Q..B....Y.
008dfff0: 3034 3730 3739 3620 3030 3030 3020 6e20  0470796 00000 n 
ID7sM2RWkyI.img
008c0000: fbf8 67fd 8162 b363 d044 5cd7 1afb 4707  ..g..b.c.D\...G.
008dfff0: 46a1 4f02 d9fe 5c69 e7df ec6f e647 95a8  F.O...\i...o.G..
jCC60mutgoE.img
008c0000: 3030 3020 6e20 0a30 3030 3030 3736 3336  000 n .000007636
008dfff0: 4dfe 74a7 70e4 8380 562b 912c 3daa 32a4  M.t.p...V+.,=.2.
RApjvIxRlu0.img
008c0000: 0a30 3030 3034 3730 3530 3520 3030 3030  .0000470505 0000
008dfff0: 34a7 831e b625 e8d4 b9e6 ecec 2c95 6949  4....%......,.iI
wlOUASom2fI.img
008c0000: 3020 5220 2f42 6f72 6465 7220 5b20 3020  0 R /Border [ 0 
008dfff0: 6e20 0a30 3030 3030 3736 3631 3020 3030  n .0000076610 00

已经很容易看出有几个是连着的了(此处建议自己找一些 PDF 文件找相似的内容比对):

  • wlOUASom2fI.img -> jCC60mutgoE.img
  • 1GHGGrmaMM0.img -> 5qiSQnlrA4Y.img -> d3Be7V0EVKo.img
  • eRL2MQSdOjo.img -> RApjvIxRlu0.img

同样方法看上一个 stripe #69(008a0000~008bffff),只有 ID7sM2RWkyI.img 是有内容的,其余几个都是全 0:

ID7sM2RWkyI.img
008a0000: 6174 7461 6368 2031 3635 350a 0000 0000  attach 1655.....
008bfff0: 3020 6f62 6a0a 3c3c 202f 4120 3438 3920  0 obj.<< /A 489 

并且会发现,这里接的下一个 stripe 很可能是 wlOUASom2fI.img:008c0000/A 489 0 R),所以 ID7sM2RWkyI.img 排最后一个,wlOUASom2fI.img 排第一个。

以上信息足够拼出第一个 PDF 文件了。三个 PDF 头只有 BOF1 是在 stripe #69 并且正是在 ID7sM2RWkyI.img。如果文件连续存储的话,它对应的文件尾只可能是 EOF1,在 jCC60mutgoE.img。这里可以恢复出来 PDF1:

$ dd if=ID7sM2RWkyI.img ibs=1 skip=9101312 count=73728 > 1.pdf
$ dd if=wlOUASom2fI.img ibs=1 skip=9175040 count=131072 >> 1.pdf
$ dd if=jCC60mutgoE.img ibs=1 skip=9175040 count=13998 >> 1.pdf

另外两组 BOF/EOF 如何配对也非常明显了:PDF2 是 BOF2 - EOF2,PDF3 是 BOF3 - EOF3。这里看 PDF3,文件头在 RApjvIxRlu0.img,下一个盘还不确定,但不妨猜一下是 ID7sM2RWkyI.img (然后进入 wlOUASom2fI.img 的下一个 stripe 到 EOF)。试着恢复文件,然后用任意 PDF 阅读器打开,验证的确是个有效的 PDF 文件:

$ dd if=RApjvIxRlu0.img ibs=1 skip=9248768 count=57344 > 2.pdf
$ dd if=ID7sM2RWkyI.img ibs=1 skip=9175040 count=131072 >> 2.pdf
$ dd if=wlOUASom2fI.img ibs=1 skip=9175040 count=17006 >> 2.pdf

至此已经推断出了盘顺序:

  1. wlOUASom2fI.img
  2. jCC60mutgoE.img
  3. 1GHGGrmaMM0.img
  4. 5qiSQnlrA4Y.img
  5. d3Be7V0EVKo.img
  6. eRL2MQSdOjo.img
  7. RApjvIxRlu0.img
  8. ID7sM2RWkyI.img

以下脚本重组 RAID:

images = ["wlOUASom2fI.img", "jCC60mutgoE.img", "1GHGGrmaMM0.img", "5qiSQnlrA4Y.img", "d3Be7V0EVKo.img", "eRL2MQSdOjo.img", "RApjvIxRlu0.img", "ID7sM2RWkyI.img"]
block_size = 128 * 1024

pos = 0
with open('output.img', 'wb') as fout:
    while True:
        for img in images:
            with open(img, 'rb') as fin:
                fin.seek(pos)
                data = fin.read(block_size)

                if len(data) == 0:
                    exit(0)

                fout.write(data)

        pos += block_size

挂载重组的磁盘镜像,拿 flag:

$ losetup -P loop0 output.img
$ mount /dev/loop0p1 /mnt/temp
$ cd /mnt/temp && python3 getflag.py

2 - RAID 5

这题其实更容易。扫一下内容就会发现有很多文本文件(比如 GRUB 代码)。这样看哪个盘乱码就很容易识别出来哪块盘在做 parity。

比如我们看 byte 00400000 前后 16 字节(之所以选这个是因为这么整的地址基本肯定是个新 stripe 的起点):

$ for f in *.img; do echo $f; xxd -s $((0x00400000-16)) -l 32 $f; done
3D8qN9DH91Q.img
003ffff0: 4453 3b20 6a2b 2b29 0a20 2020 206f 7265  DS; j++).    ore
00400000: 635f 6374 5f46 5245 4554 5950 453d 2461  c_ct_FREETYPE=$a
3RlmViivyG8.img
003ffff0: 735f 6563 686f 5f6e 2022 6368 6563 6b69  s_echo_n "checki
00400000: 594a 565e 393c 6a72 3760 3a2c 620a 4726  YJV^9<jr7`:,b.G&
60kE0MQisyY.img
003ffff0: 7564 6520 3c69 6f2e 683e 0a23 2065 6e64  ude <io.h>.# end
00400000: 745f 666c 6f61 743d 2224 6361 6e64 225d  t_float="$cand"]
IrYp6co7Gos.img
003ffff0: 6c4c 5a00 6148 631d 4813 634b 0007 1348  lLZ.aHc.H.cK...H
00400000: 6e67 2066 6f72 206f 7074 696f 6e73 2074  ng for options t
output.img
003ffff0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00400000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
QjTgmgmwXAM.img
003ffff0: 2e24 6163 5f65 7874 0a2f 2a20 656e 6420  .$ac_ext./* end 
00400000: 202d 7320 6669 6c65 2064 6972 2720 616e   -s file dir' an
test.img
003ffff0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00400000: 0000 0000 0000 0000 0000 0000 0000 0000  ................

这里很明显看出,在前一个 stripe IrYp6co7Gos.img 做 parity,然后新的 stripe 轮到 3RlmViivyG8.img 做 parity。

接下来试试 +32K、+64K、+128K…… 看看哪里是下一个 stripe 起点,就可以得到 stripe size 了(64K)。

后面就找规律了,一个 stripe 一个 stripe 往下看,结合文件连续性,很快就能得到盘的顺序,这里不再赘述过程。重组脚本如下:

import itertools

images = ["3RlmViivyG8.img", "IrYp6co7Gos.img", "3D8qN9DH91Q.img", "QjTgmgmwXAM.img", "60kE0MQisyY.img"]
block_size = 64 * 1024

pos = 0
with open('output.img', 'wb') as fout:
    while True:
        images.insert(0, images.pop())

        for j, img in enumerate(images):
            with open(img, 'rb') as fin:
                fin.seek(pos)
                data = fin.read(block_size)

                if len(data) == 0:
                    exit(0)

                if j != 0:
                    fout.write(data)

        pos += block_size