
최근 갤럭시북5 프로를 샀다. 내 것은 아니기에 스펙은 따로 적지 않는다.

노트북을 키면 이런 화면이 나온다.
하지만 순순히 윈도우를 설치하기에는 내장된 저 안내화면 표시용 OS를 그냥 지나칠 수 없었다.

켜면 잠깐 이런 화면이 나온다. 알 수 있는건 tinycore 기반의 OS라는 것이다.

놀랍게도 프리도스를 표방하면서 갤럭시북은 리눅스를 지원하지 않는다.
물론 리눅스용 드라이버도 없지만, 시큐어 부트 프리셋조차 Windows Only로 고정 시켜놔서 순순히 리눅스를 설치하도록 허용해주지 않는다.
나는 윈도우를 깔기 전, 이 OS를 기어코 뜯어보기로 했으므로 systemrescue ISO를 ventoy에 넣어 잠깐 띄워보기로 했다.
(참고로 갤럭시북 5의 무선 랜은 BE200을 사용하므로, USB에 들어 있던 우분투 22.04를 사용할 수 없었다. 한편, 26.04는 무슨 이유인지는 몰라도 패키지 리졸브 단계에서 무한로딩에 걸려 진행이 불가능했다.)

systemrescue는 여러 복구 도구 및 유틸리티들을 포함하고 있어, 만능 OS 복구 툴로 쓰기 좋다.
비단 Linux에서 주로 사용되는 FS (ext4, btrfs) 뿐만 아니라 Windows에서 주로 쓰이는 NTFS 도구도 내장되어 있다. (ntfs-3g, ntfs3)
참고로 글을 작성한 시점에서 최신 ISO 버전은 13.01였고, 해당 버전은 Kernel 6.18 LTS을 내장하고 있다.
BE200 드라이버는 Kernel 6.5부터 최소 지원하므로, 6.18이면 충분히 잡고도 남는 상황이였다.
그리고 사진으로 찍진 못했지만, 저기서 별다른 설정 없이 그냥 nmtui를 켜자마자 radio가 잡혔다.
SSID 찾고, 패스워드 넣어서 Wireless 연결하면 된다.
그리고 lsblk로 파티션을 찾고 zstd로 압축함과 동시에 nc로 서버에 보낸다.
서버측에서도 역시 nc로 TCP listen을 걸어뒀고, 그걸 file descriptor로 stdout 출력을 파일로 빼주게 셋팅해뒀다.
ssd + zstd의 힘이였는지, 네트워크 대역폭보다 속도가 더 빨랐다.

TCP 프로토콜의 오류제어를 믿고 nc로 쏘고 받았지만,
윈도우로 밀어버리면 TRIM 때문에 다시는 보지 못할 데이터일테니 sha256sum으로 재검증해줬다.

파티션을 까면 일단 이런 구조다. grub 부트로더를 통해 부팅할때 자동으로 tinycore가 켜지는 구조다.

근데, 재밌는건 $RECYCLE.BIN 같은 윈도우가 볼륨을 붙이면서 만드는 찌꺼기들이 포함되어있다는 것이다.
타임스탬프를 보아 내가 만든 것은 아닐 터, 아마 삼성 내부적으로 이 Tinycore를 개조하면서 딸려온 것 같다.

리눅스 커널은 위에서 봤다싶이 5.15.10을 사용한다.
근데, 다시 생각해보면 시큐어 부트는 출고 당시에 기본적으로 켜져있었는데 어떻게 Tinycore를 부팅할 수 있었을까? 분명 자기네들 인증서로 커널 빌드하면서 서명을 넣어놨을 것이다.

UEFI 호환성을 위해 커널은 Windows PE/COFF로 나오기 때문에, 서명은 pesign로 찍어보면 된다.
당연하게도 "Samsung Mobile Experience NC DB"로 서명되어 있다.

이제 서명에 박혀있는 인증서를 덤프해서 체인까지 살펴보자.
이 부분은 일반적인 도구로 뽑아낼 수가 없어서, AI의 힘을 빌려 스크립트를 뽑아왔다.
import subprocess, re, sys, hashlib
data = open(sys.argv[1], 'rb').read(); seen=set(); n=0
for m in re.finditer(b'\x30\x82', data): # ASN.1 SEQUENCE
i=m.start(); ln=(data[i+2]<<8)|data[i+3]; blob=data[i:i+4+ln]
if len(blob) < 4+ln: continue
if subprocess.run(['openssl','x509','-inform','DER','-noout'],
input=blob, capture_output=True).returncode: continue
h=hashlib.sha1(blob).hexdigest()
if h in seen: continue
seen.add(h); n+=1; open(f'cert_{n}.der','wb').write(blob)
s=subprocess.run(['openssl','x509','-inform','DER','-noout','-subject'],
input=blob, capture_output=True).stdout.decode()
print(f'cert_{n}.der {s.strip()}')
print(f'=> {n} certs')
재현을 위해 바이너리 서명에 박힌 인증서 추출 코드가 필요하다면, 이 코드를 그대로 사용하면 된다.
(python3, openssl은 당연하게도 깔려있어야 한다.)
굳이 모든 체인을 뒤져볼 필요는 없을것 같아, 가장 궁금한 체인의 최종 leaf 인증서만 뽑아보기로 했다.
참고로, 3개 전부 (커널, shim, grub) 뽑아본 결과 키가 똑같았기에, 그냥 아무거나 하나 찝어서 보자.

그리고 leaf 인증서의 issuer CA는 "Samsung Mobile Experience NC CA"였다.
인증서 CA는 딱히 넣어놓지 않았고, (넣을 이유도 그닥 없기에...) 글을 쓰는 시점에 노트북은 내 손을 벗어났기에 CA는 구하지 못했다.

https://bbs.archlinux.org/viewtopic.php?id=291397
갤럭시북3 에서도 동일 키로 돌려쓴 것 같은데, Subject의 Key Identifier가 일치하는 것을 확인 할 수 있다.
Not After가 2042년으로 duration이 20년인데, 진짜 20년 동안 서명으로 쓸건지는 잘 모르겠다.
지금보니 그냥 UEFI 시큐어부트 DB에 그냥 저 인증서 자체가 들어 있을수도 있겠다는 느낌이 든다.
그런거라면 펌웨어를 뜯어도 CA가 안나올텐데 말이다.

이제 인증서는 다 뜯어본 것 같으니, 저 토끼와 당근 화면이 어떻게 나오는건지만 보고 끝내면 될 것 같다.
initrmfs로 newcore.gz를 집어넣고 부팅을 하니, newcore.gz를 뜯어보자.

삼성이 만진 것 같은 스크립트 총 4개를 추려냈다.

가장 핵심적인 부분. 부팅 시 생성되는 유저의 home으로 복사되서 해당 user로 로그인되며 자동으로 이 스크립트가 실행되는 방식이다.
얼핏 봐도 L44-L61 부분이 눈길이 가게 된다.
해당 이미지가 마운트 된 위치에서 바로 startup-image.bmp가 있었기에 그걸 경로로 잡아 fbv로 이미지를 띄우는게 끝이다.
fbv (아마 FrameBufferViewer?) 명령은 프레임버퍼 뷰어로써, 커널 내장 그래픽 출력 방식인 프레임버퍼로 이미지 띄우는 프로그램이다.
근데 지금보면 fbv 실행하기 전에, /dev/ttyS0로 로그를 쏴주는 것 같은데, 이건 다른 곳에서 연결을 잡아둔다.

부팅할때 /dev/ttyS0에 시리얼 포트를 잡아둔다. 물론 이것도 부팅시에 실행된다.
/dev/ttyS0로 로그를 날리는듯 한데, 지금 파티션 이미지에 보이는건 "acpid start"랑, 아까 그 fbv 실행 전에 날리는 "startup-image.bmp is displayed" 밖에 찾지 못했다.
아마 이건 QC용으로 출고전에 한번 부팅되는지 테스트해보는 용도 아니였을까?


bootlocal.sh과 autologin은 이렇게 생겼다. 그닥 볼게 없어서 설명 할 필요는 없어보인다.