2016년 11월 22일 화요일

SQLite 데이터베이스의 구조적 특징과 데이터 복구


[Tech Report] ‘구조’를 알면 ‘복구 가능성’이 보인다



  • AhnLab


  • 2014-05-09

SQLite 데이터베이스의 구조적 특징과 데이터 복구





지난 월간 안 4월호에서는 스마트폰의 성장과 함께 디지털 포렌식 전문가들에게 주목 받고 있는 SQLite 데이터베이스에 대해 알아보았다. SQLite는 데이터베이스 레코드를 하나의 파일에 저장하여 관리한다. 데이터베이스를 운용하다 보면 레코드를 추가ㆍ삭제ㆍ변경하는 일이 빈번하게 발생한다. SQLite의 경우 레코드를 삭제할 때 삭제된 레코드가 있었던 영역을 사용하지 않는(비할당) 영역으로 구분하지만 해당 영역이 반드시 초기화 되는 것은 아니다. 물론 ‘Vacuum’이라는 삭제된 영역 청소 기능을 자동으로 설정해 놓은 경우 레코드 삭제와 동시에 해당 영역이 초기화 되지만, 성능 향상을 위해 ‘Vacuum’ 기능을 사용하지 않는 경우가 대부분이다. 즉, 운용되고 있는 SQLite 데이터베이스 파일을 살펴보면 삭제된 레코드의 흔적을 발견할 수 있는 경우가 많다는 것이다. 이와 관련해 SQLite 파일 내부에서 삭제된 데이터의 흔적을 추적하는 방법에 대해서 알아보자.




<연재 목차>

1부_스마트폰 포렌식과 SQLite, 포렌식 관점에서의 의미(2014년 4월 호)

2부_SQLite 데이터베이스의 구조적 특징과 데이터 복구(이번 호)

3부_SQLite의 레코드 구조와 삭제된 데이터 복구 방법

4부_스마트폰 인스턴트 메신저 및 문자 메시지 복구하기




SQLite 데이터베이스 파일의 구조


우선, 레코드의 위치를 파악하는 과정을 이해하기 위하여 SQLite 데이터베이스 파일의 전체 구조부터 살펴보자. [그림 1]은 SQLite 데이터베이스 파일의 구조를 개략적으로 표현한 것이다. SQLite 데이터베이스 파일은 페이지 단위로 나뉘어 구성된다. 파일의 최상단에 위치한 헤더 페이지에는 데이터베이스의 속성 정보를 담고 있는 ‘데이터베이스 헤더(Database Header)’와 데이터베이스의 테이블 구조를 담고 있는 ‘스키마 테이블(Schema Table)’이 존재한다. 파일 내부에 존재하는 레코드의 위치를 추적하기 위해서는 스키마 테이블의 내용을 주의 깊게 살펴보아야 한다.

 



[그림 1] SQLite 데이터베이스 파일 구조


[그림 2]는 실제 스키마 테이블의 예시 화면이다. [그림 2]와 같이 스키마 테이블은 스키마가 생성될 당시 데이터베이스에 전달된 SQL쿼리를 문자열 형태로 저장한다. 여기서 주목해야 할 요소는 쿼리 문자열 바로 앞의 숫자이다. SQLite는 하나의 스키마를 통해 만들어진 데이터 구조(테이블, 인덱스 등)를 [그림 3]과 같이 B-트리(B-Tree) 형태로 관리하는데, 트리(Tree)의 노드(Node) 당 하나의 페이지가 매핑된다. 이때 쿼리 문자열 바로 앞의 숫자는 B-트리의 루트(Root)를 나타낸다.

 

 



[그림 2] 스키마 테이블의 구조


[그림 3]은 하나의 스키마에 연결된 페이지의 구조를 나타낸 것이다. 스키마 테이블의 쿼리 문자열 앞에는 루트 페이지의 번호가 적혀있고, 루트 페이지를 기점으로 하위 페이지들이 연결되어 있다. 여기에서 레코드 데이터를 담고 있는 리프(Leaf) 페이지와 각 페이지의 연결을 담당하고 있는 인터널(Internal) 페이지의 구분을 눈 여겨 볼 필요가 있다.




디지털 포렌식에서 관심을 갖는 레코드 데이터는 모두 리프 페이지에 존재하며, 인터널 페이지는 리프 페이지까지의 연결 고리를 제공할 뿐 실제 레코드 데이터는 포함하고 있지 않다. 따라서 삭제된 레코드를 복원하기 위해서는 우선 리프 페이지에 접근해야 한다.

 

 

[그림 3] SQLite 데이터베이스 파일 내 페이지들의 트리 구조


인터널 페이지와 리프 페이지



SQLite 데이터베이스 파일에서 인터널 페이지와 리프 페이지는 각 페이지의 플래그(최상위 1바이트)로 구분된다. [그림 4]와 [그림 5]는 각각 인터널 페이지와 리프 페이지의 헤더 구조를 나타낸다. 인터널 페이지는 페이지 플래그로 ‘0x05’를 가지며, 리프 페이지는 ‘0x0D’를 페이지 플래그로 가진다.




따라서 SQLite 데이터베이스 파일을 발견한 포렌식 담당자는 스키마 테이블에 연결된 트리 구조를 해석하거나 페이지의 크기 단위로 파일을 순차 탐색하며 ‘0x0D’의 플래그를 갖는 페이지를 조사하는 방식으로 데이터베이스 레코드가 포함된 영역을 구분할 수 있다. 이렇게 ‘0x0D’를 플래그로 갖는 페이지를 SQLite에서는 ‘데이터 페이지’라 한다.

 

 



[그림 4] 인터널 페이지의 헤더 구조

 



[그림 5] 리프 페이지의 헤더 구조


삭제된 레코드가 존재하는 영역

[그림 6]은 데이터 페이지에 레코드가 존재하는 방식과 삭제된 영역이 존재할 수 있는 위치를 나타낸다. 데이터 페이지 내부에는 셀(Cell)이라 불리는 하위 저장 구조가 존재한다. 셀은 페이지의 하단에서부터 차례로 쌓이며, 페이지 상단의 헤더에 이어 각 셀의 위치를 나타내는 오프셋 배열이 나타난다.


이제 삭제된 영역을 탐색하기에 앞서 SQLite 데이터 페이지의 비할당 영역에 대해 알아보자. SQLite 데이터 페이지에는 크게 두 가지 비할당 영역이 존재한다. 첫 번째는 ‘비할당 공간(Free Space)’으로, 셀 오프셋과 셀 사이의 영역을 의미한다. 데이터 페이지에서 셀은 페이지 최하단부터 쌓이고, 셀 오프셋은 페이지 헤더 다음에 차례로 나타나므로 셀과 셀 오프셋 사이에는 항상 사용되지 않은 공간이 존재하는데, 이를 비할당 공간이라 부른다.


또 하나의 비할당 영역은 ‘비할당 블록(Free Block)’이다. 비할당 블록은 셀이 페이지 하단부터 순차적으로 쌓이는 과정에서 중간에 쌓여있던 셀이 삭제되었을 때 생성되는 공간이다. 이 영역은 차후에 삭제된 레코드보다 크기가 작은 레코드가 데이터베이스에 삽입될 경우 사용할 수 있도록 페이지 헤더에서 관리한다.


이와 같이 페이지 내부에서 데이터와 데이터 오프셋을 양쪽에서 자라나가게 하며 중간의 빈 공간을 점차 소진해 나가는 방식은 MS-SQL과 Mac OS X의 파일 시스템(HFS+) 등과 같이 B-트리 구조로 데이터를 저장하고자 하는 경우 B-트리의 노드 내부에서 자주 사용되는 방식이다. 따라서 SQLite를 통해 분석 방법을 익혀둔다면 또 다른 디지털 포렌식 분석에도 도움이 될 것이다. 

 

 

 ㅇㅇㅇ

[그림 6] SQLite 데이터 페이지의 비할당 공간과 비할당 블록 위치


이제 데이터 페이지 내부에서 삭제된 데이터 영역을 구분할 차례다. (물론, 데이터 페이지와 그 비 할당 영역까지 구분 및 이해한 보안 연구원이라면 이미 삭제된 영역의 구분도 이해하고 있을 것이다.)

 



                                             ㅇㅇㅇ

[그림 7] 데이터 페이지 에서 삭제된 영역



[그림 7]은 데이터 페이지에 존재할 수 있는 3가지 삭제된 데이터의 유형을 나타낸다. 먼저 삭제된 영역인 (1)과 (2)는 비할당 블록에 존재하는 데이터를 나타낸 것이다. 비할당 블록은 이미 한번 할당된 후 삭제되어 셀과 셀 사이에 존재하는 영역이므로, 그 존재 자체가 삭제된 데이터를 의미한다. 단, 삭제된 영역 (1)은 비할당 블록이 생성된 후 비할당 블록보다 작은 크기의 레코드가 데이터베이스에 삽입된 경우를 나타내는데, 이 경우 레코드 상단의 데이터가 크게 손실되어 복구를 진행할 수 없는 경우가 많다.


하지만 삭제된 영역 (2)와 같이 레코드가 삭제된 후 덮어 쓰이지 않았다면 레코드의 구조적 특성을 이용하여 내부 데이터를 전부 추출할 수 있다. 따라서 데이터 페이지를 발견한 포렌식 담당자는 비할당 블록을 순차적으로 탐색하며 레코드 복원 가능 여부의 확인 후 복원을 시도하여야 한다. 다행히 SQLite는 [그림 8]과 같이 비할당 블록을 체인 형태로 유지하고 있어 포렌식 담당자는 비할당 블록 체인을 순회하는 것으로 데이터 페이지 내부의 비할당 블록을 전부 확인할 수 있다.


끝으로, 삭제된 영역 (3)은 비할당 공간에 존재하는 삭제된 영역이다. 기본적으로 SQLite는 페이지를 새로 할당할 때 모든 데이터를 0으로 초기화하여 할당한다. 이는 비할당 공간에서 삭제된 영역을 찾는데 이용할 수 있는데, 셀 오프셋과 셀 사이의 영역에서 0이 아닌 데이터를 추출하면 비할당 영역의 삭제된 데이터가 추출되는 것이다. 

 




[그림 8] SQLite 데이터 페이지의 비할당 블록 체인


삭제된 영역의 탐색 및 레코드의 복원


SQLite 데이터베이스 파일을 접한 디지털 포렌식 담당자는 다음과 같은 절차를 통해 데이터베이스 내부의 삭제된 레코드가 존재하는 영역을 파악할 수 있다.


1. ‘0x0D’ 플래그를 이용한 데이터 페이지의 탐색

2. 비할당 블록 체인을 활용한 비할당 블록 탐색 및 추출

3. 비할당 공간을 탐색하여 0이 아닌 데이터 추출


이와 같은 과정을 거쳐 삭제된 영역의 데이터를 추출한 후에는 레코드의 구조를 분석하여 각 필드의 데이터를 확인할 필요가 있다. SQLite는 여러 가지 구조적 기법을 동원하여 레코드를 관리하고 있다. 이는 데이터베이스의 경량화라는 목적을 달성하기 위함이지만, 디지털 포렌식에 있어서는 구조를 파악하기 위해 필요한 정보가 레코드 내부에 다양하게 존재하고 있음을 의미한다.


[그림 9]는 SQLite의 레코드 구조를 나타낸다.

 




[그림 9] SQLite 레코드 구조


이 글에서는 SQLite 데이터베이스의 구조적 특징을 이해함으로써 삭제된 데이터의 위치와 복구 가능성을 파악하는 방법을 살펴보았다. 다음호에서는 삭제된 이후 레코드의 변화와 레코드의 구조적인 특성을 이용해 각 필드의 내용을 복원하는 과정을 알아볼 예정이다.@










  • AhnLab 로고



  • 클라우드 분석팀 전상준 연구원




이 정보에 대한 저작권은 AhnLab에 있으며 무단 사용 및 도용을 금합니다.

단, 개인이 비상업적인 목적으로 일부 내용을 사용하는 것은 허용하고 있으나, 이 경우 반드시 출처가 AhnLab임을 밝혀야 합니다.

기업이 이 정보를 사용할 때에는 반드시 AhnLab 의 허가를 받아야 하며, 허가 없이 정보를 이용할 경우 저작권 침해로 간주되어 법적인 제재를 받을 수 있습니다. 자세한 내용은 컨텐츠 이용약관을 참고하시기 바랍니다.

정보 이용 문의 : contents@ahnlab.com


홈페이지 jQuery 라이브러리에서 CVE-2019-11358 취약점 패치 여부 확인 방법

현재 홈페이지에서 사용 중인 jQuery 라이브러리가 CVE-2019-11358 취약점 패치를 적용했는지 확인하는 방법은 다음과 같습니다. 1. jQuery 버전 확인 홈페이지 소스 코드를 확인하여 jQuery 라이브러리 버전을 직접 확인합니다. 웹 ...