[2025 DFC] 2025 디지털포렌식 챌린지 - 207 – iBackup Trace
| Step-by-step methodology: |
문제 풀이에 앞서, dfchallenge.org에서 제공한 문제 파일의 해시값과 다운로드 받은 파일의 해시값을 비교하여 동일성을 검증하였다.

그림 1 dfchallenge.org에 명시되어 있는 MD5 해시값

그림 2 HashTab을 사용해 다운로드 받은 파일의 MD5 해시값
주어진 파일은 backup.zip 파일이며 상세 정보는 다음과 같다.
| FileName | backup.zip |
| Size | 131,621,229 Bytes |
| MD5 | d4591cd1aa35101c2484bc6a6d986538 |
표 1 주어진 파일의 상세 정보
압축을 해제하면 `00008130-001E682D11181001C` 폴더가 생성되며, 하위 폴더의 예시는 다음과 같다.

그림 3 압축 해제 된 파일의 일부 폴더
문제에서 주어진 파일들은 iTunes를 통한 아이폰 논리 백업파일의 형태이며, 일반적인 백업 파일과 다르게 Manifest.db 등이 0Bytes로 설정되어 문제 내용과 같이 일부 내용은 복구 되지 못한 모습을 보여주고 있다.

그림 4 비정상적으로 Manifiest.db 파일등이 0bytes로 되어 있는 모습
문자 메시지 기록과 연락처를 복구하기 위해서는 각각 어떤 파일과 매핑되는지를 먼저 확인해야 한다. 이를 위해 문제 시나리오와 유사한 환경에서 직접 아이폰 논리 백업을 수행하였다. 논리 백업이 완료된 후, Manifest.db 파일을 분석하여 문자 메시지 및 연락처 데이터베이스의 실제 저장 경로를 확인하였다.

그림 5 비슷한 환경에서 논리 백업 후 Manifest.db 파일 내 sms.db 문자열 검색 결과

그림 6 비슷한 환경에서 논리 백업 후 Manifest.db 파일 내 AddressBook.sqlitedb 문자열 검색 결과
이 과정에서 확인된 fileID 값은 SHA1 해시를 기반으로 생성된다. 해시 생성 방식은 Manifest.db의 Files 테이블에서 domain 컬럼과 relativePath 컬럼을 “-”로 연결한 문자열을 입력값으로 하여 SHA1 알고리즘을 적용하는 방식이다.
| import hashlib def string_to_sha1(s) : return hashlib.sha1(s.encode('utf-8')).hexdigest() # "3d0d7e5fb2ce288813306e4d4636395e047a3d28" print(string_to_sha1("HomeDomain" + "-" + "Library/SMS/sms.db")) # "31bb7ba8914766d4ba40d6dfb6113c8b614be442" print(string_to_sha1("HomeDomain" + "-" + "Library/AddressBook/AddressBook.sqlitedb")) |
표 2 특정 경로를 sha1으로 변환 검증 코드
각각 문제에서 필요한 데이터베이스의 파일 해쉬값은 다음과 같다.
| Type | SHA1 |
| SMS | 3d0d7e5fb2ce288813306e4d4636395e047a3d28 |
| AddressBook | 31bb7ba8914766d4ba40d6dfb6113c8b614be442 |
표 3 문제에서 필요한 SHA1 해시값 (fileID)
- 총 몇개의 메세지가 존재하는가? (50점)
SMS 데이터베이스는 3d0d7e5fb2ce288813306e4d4636395e047a3d28 파일에 존재하며, 실제 파일명은 sms.db이다. 이하 본 보고서에서는 이를 sms.db 파일이라 칭한다.
해당 데이터베이스는 DB Browser for SQLite 도구를 이용하여 분석하였다. sms.db의 message 테이블을 조회함으로써 전체 문자 메시지 내역을 확인할 수 있었다.

그림 7 DB Browser for SQLite를 통한 sms.db 파일 내 message 테이블의 일부
| SELECT COUNT(*) FROM message; |
표 4 전체 행 개수를 계산 하기 위한 SQL 쿼리

그림 8 SQL 쿼리 결과
정답 : 131개
2. 총 몇 명이 주소록에 저장되어있는가? (50점)
주소록 데이터베이스는 31bb7ba8914766d4ba40d6dfb6113c8b614be442 파일에 존재하며, 실제 파일명은 AddressBook.sqlitedb이다. 이하 본 보고서에서는 이를 AddressBook 파일이라 칭한다.
해당 데이터베이스는 DB Browser for SQLite 도구를 통해 분석하였다. 주소록에 저장된 인원 정보는 ABPerson 테이블에 기록되어 있으며, 이를 기반으로 전체 연락처 내역을 확인할 수 있다.

그림 9 DB Browser for SQLite를 통한 AddressBook 파일 내 ABPerson 테이블의 일부
| SELECT COUNT(*) FROM ABPerson; |
표 5 전체 행 개수를 확인하기 위한 SQL 쿼리

그림 10 SQL 쿼리 결과
3. 지인으로부터 온 메세지의 제목과 내용을 알아내시오. (50점)
AddressBook 파일에서는 총 12명의 주소록을 확인할 수 있으며, 문제에서 의미하는 지인은 A씨의 주소록에 저장된 사람을 의미하는 것으로 추정된다. 따라서 AddressBook 파일에 존재하는 주소록을 기반으로 분석을 진행한다.
아래 사진은 AddressBook 파일의 ABPerson 테이블의 일부 칼럼의 내용이다.

그림 11 AddressBook 파일 내 ABPerson 테이블 일부
위 사진에서는 주소록에 저장된 실제 전화번호를 확인할 수 없으며, ABMultiValue 테이블에서 실제 연락처 정보를 확인할 수 있다.
아래 사진은 AddressBook 파일의 ABMultiValue 테이블의 일부 칼럼의 내용이다.

그림 12 AddressBook 파일 내 ABMultiValue 테이블 일부
해당 데이터베이스 구조를 도식화하면 다음과 같다.

그림 13 AddressBook 파일의 구조 도식화
AddressBook 파일의 ABPerson 테이블과 ABMultiValue 테이블이 연관이 있는 테이블이다.
ABPerson의 ROWID가 ABMultiValue 테이블의 record_id와 연결된다. 따라서 아래와 같은 주소록 정보를 얻을 수 있다.
| ABPerson-ROWID | ABPerson-FIRST | ABPerson-LAST | ABMultiValue-value |
| 1 | Lisa | Yo | 010990058411 |
| 2 | James | Ahn | 010210084511 |
| 3 | Emily | Smith | 010850861301 |
| 4 | Michael | Johnson | 010514438041 |
| 5 | Olivia | Williams | 010905861151 |
| 6 | Ethan | Brown | 010378812541 |
| 7 | Ava | Jones | 010667438511 |
| 8 | Daniel | Garcia | 010455304201 |
| 9 | Sophia | Miller | 010751292861 |
| 10 | Benjamin | Davis | 010385699121 |
| 11 | Mia | Wilson | 010985233641 |
| 12 | Lucas | Anderson | 010318424451 |
표 6 ABPerson과 ABMultiValue를 조합한 정리 테이블
위 표에서 확인한 전화번호를 바탕으로 DB Browser for SQLite 도구를 통해 SMS파일의 message 테이블에서 필터링 한 결과 010751292861 번호에서만 1개의 메시지가 발견되었다. 이 전화번호는 message 테이블에 fallback_hash 칼럼에 다음과 같은 형식으로 저장되어 있다.

그림 14 sms.db 내 message 테이블에서 발견된 751292861 정보
message 테이블의 구조는 다음과 같다.
| 컬럼명 | 설명 | 활용 포인트 |
| ROWID | 각 메시지의 고유 식별자 (PK) | 다른 테이블(message_attachment_join)과 매핑 시 기준 키 |
| guid | 메시지 고유 식별자 (UUID) | iMessage/DB 간 메시지 추적 |
| text | 문자 메시지 본문 | 실제 송수신된 메시지 내용 |
| subject | 메시지 제목 | MMS 등 제목 포함 메시지 분석 시 |
| handle_id | 발신/수신자 식별 ID (handle 테이블과 매핑) | 송수신 상대 확인 |
| date | 메시지 생성(전송) 시각 (epoch time) | 타임라인 분석 |
| date_read | 읽은 시각 | 수신자 확인 여부 추적 |
| date_delivered | 배달 완료 시각 | 전송 성공 여부 확인 |
| is_from_me | 내가 보낸 메시지 여부 (1=보낸 메시지, 0=수신 메시지) | 송·수신 구분 |
| is_sent | 메시지 전송 완료 여부 | 실제 전송 성공 여부 판단 |
| is_read | 메시지 읽음 여부 | 확인/미확인 상태 분석 |
| service | 서비스 유형 (예: SMS, iMessage) | 통신 방식 구분 |
| account / account_guid | 사용된 계정 정보 | Apple ID, iCloud 연계 확인 |
| cache_has_attachments | 첨부파일 존재 여부 (0=없음, 1=있음) | 첨부 추적 여부 판단 |
| fallback_hash | 전화번호/세션/해시가 혼합된 값 | 발신자 번호 추적, 데이터 복구 단서 |
표 7 sms.db 내 message 테이블의 구조
| s:tel:+8210751292861|0(28)<6F04EC061108B8DA6F7852BEC387D9800A6A2AEE55BABB4BEB5A32DC448881A6> |
표 8 fallback_hash 칼럼에서 발견된 010751292861 정보
Fallback_hash의 구조는 다음과 같다.
s:tel:+8210751292861 : 전화번호가 국제 표준(E.164) 형식으로 저장된 부분이며, +82는 대한민국 국가번호를 의미한다.
|0(28) : 메시지 전송 세션과 관련된 내부 식별자를 의미한다.
<...> : 송수신 시점에서 메시지 무결성 검증 및 추적을 위한 해시값 형태이다.
즉, fallback_hash는 전화번호를 국제 표기 방식으로 변환한 후, 메시지 전송 과정의 고유 식별자와 해시값을 함께 저장하는 필드로 볼 수 있다. 이를 통해 단말기 간 동기화 또는 데이터베이스 복구 과정에서 중복 메시지를 구분하거나 무결성을 확인할 수 있다.
| 제목 |
| Please confirm your order details |
| 내용 |
| [Web Message] 📦 Transfer Path Activated — Clean Lane, Silent Move Greetings, Mate. This isn’t a pitch. It’s a notice. If you're reading this, your node is trusted and your line is considered green. Our infrastructure handles mid- to high-volume dispatches, including flagged stacks. We process across primary, relay, and ghost channels, depending on timing and pressure. 🌍 Primary Gateway: http://70.com/login.asp 🔐 Clearance Code: 555 ✅ Release-on-trigger system — no confirmations needed post-signal ✅ Zero KYC enforcement on receiver side ✅ Currency-neutral transfer (₩→$, $→€, or internal credits) ✅ Routing auto-adjusts based on throughput; congested lines reassign in real time ✅ Split delivery optional — main + buffer payload if flagged 💼 Standard dispatch: up to ₩1B per cycle 💳 Parallel links yield up to 1.2% 📊 Full log trace available on backchannel 📄 Ledger access given via secondary hash (on request only) If your recipient requires obscured receipt, enable blind handoff mode in your key panel. Relay points are scrubbed at 48-hour intervals. No residuals. No trace. We’re not a brand. We’re a mechanism. Run a 100K test cycle — it’ll move cleaner than any front you’ve dealt with. |
표 9 문제 3번의 정답
5. 해당 메시지에 첨부된 것으로 추정되는 파일의 저장된 파일명을 찾으시오. (50점)
sms.db 파일에는 문자 메시지 송수신 내역이 저장되어 있으며, 이를 기반으로 전체 대화 기록을 확인할 수 있다. 아래 그림은 SMS 데이터베이스의 일부 구조를 도식화한 것이다.

그림 15 sms.db 파일의 구조 도식화
sms.db의 message 테이블에서 각 레코드는 고유 식별자인 ROWID를 가진다. 이 ROWID는 message_attachment_join 테이블의 message_id와 1:1로 매핑된다. 또한, message_attachment_join 테이블의 attachment_id는 attachment 테이블의 ROWID와 1:1 연관 관계를 형성한다.
message_attachment_join 테이블은 문자 메시지(message 테이블)와 첨부파일(attachment 테이블) 간의 관계를 정의한다. 각 메시지는 0개 이상의 첨부파일을 가질 수 있으며, 하나의 첨부파일이 여러 메시지와 연결될 수도 있다. 이러한 관계를 관리하기 위해 중간 매핑 테이블이 사용된다.
| 컬럼명 | 타입 | 설명 |
| message_id | INTEGER | message 테이블의 ROWID (외래키) |
| attachment_id | INTEGER | attachment 테이블의 ROWID (외래키) |
표 10 message_attachment_join 테이블의 구조
attachment 테이블은 문자 메시지에 첨부된 파일의 메타데이터를 저장한다. 주요 컬럼의 의미는 다음과 같다.
| 컬럼명 | 타입 | 설명 |
| ROWID | INTEGER | 각 첨부파일 레코드의 고유 식별자 (기본키) |
| guid | TEXT | 첨부파일의 전역 고유 식별자 (UUID 형식) |
| created_date | INTEGER | 첨부파일 생성 시각 (UNIX 타임스탬프) |
| start_date | INTEGER | 첨부파일 전송 시작 시각 |
| filename | TEXT | 첨부파일 저장 경로 |
| uti | TEXT | Uniform Type Identifier (ex. public.png) |
| mime_type | TEXT | MIME 타입 (ex. image/png) |
| transfer_state | INTEGER | 첨부 전송 상태 코드 |
| is_outgoing | INTEGER | 송신 여부 (1=보낸 첨부, 0=수신 첨부) |
| user_info | BLOB | 사용자 관련 메타데이터 |
| transfer_name | TEXT | 전송된 실제 파일명 |
| total_bytes | INTEGER | 첨부파일 크기 (bytes 단위) |
| is_sticker | INTEGER | 스티커 여부 |
| attribution_info | BLOB | 메시지 출처 정보 |
| ck_sync_state | INTEGER | CloudKit 동기화 상태 |
| ck_record_id | TEXT | CloudKit 레코드 ID |
| original_guid | TEXT | 원본 GUID (중복 방지를 위해 UNIQUE 제약 적용) |
| is_commsafety_sensitive | INTEGER | 민감 콘텐츠 여부 표시 |
| emoji_image_content_identifier | TEXT | (이모지 이미지의 경우) 콘텐츠 식별자 |
| emoji_image_short_description | TEXT | (이모지 이미지의 경우) 짧은 설명 |
| preview_generation_state | INTEGER | 미리보기 생성 상태 |
표 11 attachment 테이블의 구조
따라서, 3번 문제에서 확인한 메시지의 ROWID 값을 기준으로 message_attachment_join 테이블을 조회하면 해당 메시지와 연결된 첨부파일 정보를 확인할 수 있다.

그림 16 sms.db 파일 내 message_attachment_join 테이블의 일부
3번 문제에서 확인된 문자 메시지의 ROWID는 125이며, 해당 메시지는 attachment_id = 11과 연관되어 있다. 따라서 attachment 테이블에서 ROWID = 11인 레코드를 조회하면 해당 메시지에 첨부된 파일 정보를 확인할 수 있다.

그림 17 sms.db 파일 내 attachment 테이블의 일부 및 ROWID 11 정보
해당 데이터(ROWID=11)를 추출한 결과는 아래와 같다.
| 컬럼명 | 값 |
| ROWID | 11 |
| guid | 8B37AEFD-D9D3-401E-8A73-8DB82BBAB25D |
| filename | ~/Library/SMS/Attachments/c7/07/8B37AEFD-D9D3-401E-8A73-8DB82BBAB25D/Resized_1753100894543_1753101056921.png |
| uti | public.png |
| mime_type | image/png |
| transfer_name | Resized_1753100894543_1753101056921.png |
| total_bytes | 567599 |
표 12 ROWID가 11인 데이터
분석 결과, 해당 메시지에 첨부된 것으로 확인되는 파일명(transfer_name 컬럼)은 다음과 같다.
정답 : Resized_1753100894543_1753101056921.png