rubrapack 매뉴얼←↑→

31 복합 파일(Compound File Binary, CFB)

.msi 파일은 복합 파일("구조적 저장소")이다: 파일 하나 안에 이름 붙은 스트림들이 든 작은 파일 시스템이다. 형식은 [MS-CFB]가 정한다. 이 문서는 MSI 작성기에 필요한 부분과 Windows 자신의 작성기가 하는 일을 다룬다.

31.1 섹터#

체인과 표 속의 특별한 섹터 번호: [spec]

값뜻
FFFFFFFA가장 큰 보통 섹터 번호
FFFFFFFCDIFAT 섹터(FAT 안에서)
FFFFFFFDFAT 섹터(FAT 안에서)
FFFFFFFE체인의 끝
FFFFFFFF빈 섹터 / 스트림 없음

31.2 머리(처음 512바이트) [spec]#

오프셋크기필드쓰는 값
08서명D0 CF 11 E0 A1 B1 1A E1
816CLSID0
242부판003E
262주판3 또는 4
282바이트 순서FFFE(바이트 FE FF)
302섹터 시프트9 또는 12
322미니 섹터 시프트6(64바이트 미니 섹터)
346예약0
404디렉터리 섹터 수3판은 0, 4판은 그 수
444FAT 섹터 수
484첫 디렉터리 섹터
524트랜잭션 서명0
564미니 스트림 경계4096
604첫 미니 FAT 섹터없으면 FFFFFFFE
644미니 FAT 섹터 수
684첫 DIFAT 섹터없으면 FFFFFFFE
724DIFAT 섹터 수
76436처음 109개 FAT 섹터 번호(DIFAT)안 쓰는 칸은 FFFFFFFF

31.3 FAT, DIFAT, 미니 FAT [spec]#

FAT 는 자기 섹터와 DIFAT 의 섹터까지 덮어야 한다. 작성기는 수가 더 바뀌지 않을 때까지 되풀이해 셈한다.

31.4 디렉터리 [spec]#

디렉터리는 128바이트 항목을 담은 섹터 체인이다. 항목 0 이 루트다.

오프셋크기필드
064이름, UTF-16LE, NUL 로 끝남(최대 31자 + NUL)
642NUL 을 포함한 이름 길이(바이트)
661종류: 0 안 씀, 1 저장소, 2 스트림, 5 루트
671색: 0 빨강, 1 검정
684왼쪽 형제(FFFFFFFF = 없음)
724오른쪽 형제
764자식(저장소와 루트만)
8016CLSID
964상태 비트
1008만든 시각
1088고친 시각
1164시작 섹터(크기에 따라 보통 또는 미니)
1208스트림 크기(3판에서는 아래 32비트만 센다)

31.5 단순하고 결정적인 작성기#

rubrapack 은 파일을 다음 순서로, 모든 구간을 이어서 놓는다:

머리 | FAT 섹터 | DIFAT 섹터 | 디렉터리 | 미니 FAT | 미니 스트림 | 큰 스트림들을 차례로
  1. 스트림 크기를 "미니"(< 4096)와 "보통"으로 나눈다.
  2. 미니 스트림들은 64바이트 단위로 이어 붙인다. 미니 스트림 길이는 그 합이다.
  3. 디렉터리, 미니 FAT, 미니 스트림 섹터 수를 세고, 그다음 FAT 와 DIFAT 섹터 수를 센다(되풀이).
  4. 이어진 구간마다 FAT 체인을 채우고, 디렉터리 트리, 미니 FAT, FAT, DIFAT, 머리를 쓴다.

31.6 안전하게 읽기#

판독기는 다음을 따라가지 말고 거부해야 한다: 도는 체인(파일의 섹터 수 안에 FFFFFFFE 에 닿지 않는 체인), 파일 밖의 섹터 번호, 스트림 크기가 요구하는 것보다 짧은 체인, 범위 밖의 디렉터리 링크, 한 노드를 두 번 지나는 형제 트리, 상식적인 한도를 넘는 수. 어느 것도 비용이 크지 않고, 그렇지 않으면 조작된 .msi 가 판독기를 멈추게 하거나 죽인다.

31.7 실제 예: 튜토리얼의 hello.msi#

튜토리얼의 첫 패키지(2장)를 --reproducible 로 빌드하면 139264 바이트다: 머리 섹터와 4096 바이트 섹터 33개. 머리는:

오프셋바이트필드값
0x00d0 cf 11 e0 a1 b1 1a e1서명Compound File
0x183e 00부 판0x003E
0x1A04 00주 판4
0x1Cfe ff바이트 순서0xFFFE
0x1E0c 00섹터 시프트12: 2^12 = 4096
0x2006 00미니 섹터 시프트6: 64
0x2801 00 00 00디렉터리 섹터 수1
0x2C01 00 00 00FAT 섹터 수1
0x3001 00 00 00첫 디렉터리 섹터1
0x3800 10 00 00미니 스트림 경계4096
0x3C02 00 00 00첫 미니 FAT 섹터2
0x4001 00 00 00미니 FAT 섹터 수1
0x44fe ff ff ff첫 DIFAT 섹터FFFFFFFE
0x4C00 00 00 00DIFAT[0]: 첫 FAT 섹터0

섹터들과 각 섹터의 FAT 값(사슬의 다음 섹터):

섹터파일 오프셋FAT 값담은 것
00x1000FFFFFFFDFAT
10x2000FFFFFFFE디렉터리
20x3000FFFFFFFE미니 FAT
30x40004미니 스트림
40x50005미니 스트림
50x6000FFFFFFFE미니 스트림
60x70007Binary.RpCa
70x80008Binary.RpCa
80x90009Binary.RpCa
90xA00010Binary.RpCa
100xB00011Binary.RpCa
110xC00012Binary.RpCa
120xD00013Binary.RpCa
130xE00014Binary.RpCa
140xF00015Binary.RpCa
150x1000016Binary.RpCa
160x1100017Binary.RpCa
170x1200018Binary.RpCa
180x1300019Binary.RpCa
190x1400020Binary.RpCa
200x1500021Binary.RpCa
210x1600022Binary.RpCa
220x1700023Binary.RpCa
230x1800024Binary.RpCa
240x1900025Binary.RpCa
250x1A00026Binary.RpCa
260x1B00027Binary.RpCa
270x1C00028Binary.RpCa
280x1D00029Binary.RpCa
290x1E00030Binary.RpCa
300x1F000FFFFFFFEBinary.RpCa
310x2000032cab1.cab
320x21000FFFFFFFEcab1.cab

뿌리 디렉터리 항목, 섹터 1 의 처음 128 바이트:

오프셋바이트필드값
052 00 6f 00 6f 00 74 00 20 00 45 00 6e 00 74 00 72 00 79 00 ...이름Root Entry (UTF-16LE)
6416 00이름 길이22
6605종류5 (root)
6701색1 (black)
7607 00 00 00자식항목 7
8084 10 0c 00 00 00 00 00 c0 00 00 00 00 00 00 46CLSID{000C1084-0000-0000-C000-000000000046}
11603 00 00 00시작섹터 3
12000 22 00 00 00 00 00 00크기8704

뿌리의 크기가 곧 미니 스트림의 크기다: 8704 바이트에 스트림 23개 중 21개가 64 바이트 미니 섹터로 들어 있고, 보통 섹터를 쓸 만큼 큰 스트림은 2개다. 스트림의 이름과 내용은 복합 파일 속의 MSI 데이터베이스가 이어서 보인다.