Google Drive for Windows client artifacts analysis - 2

해당 글은 이종찬 연구원이 작성한 글입니다.

앞선 포스팅에서 정리한 실험 항목을 바탕으로 검증을 진행했다. 실험 전 구글 드라이브 서버에는 다음과 같은 구조로 파일 및 폴더를 설정했다. 이는 이전 포스팅에서 정리한 동기화 폴더 설정 : 구글 드라이브 서버 → 로컬 폴더와 관련 있다. 즉, 앞의 설정을 통해 동기화 시 서버 내의 파일 및 폴더가 설정된 로컬 폴더에 저장되는 구조이다.

# 구글 드라이브 서버 내 파일 및 폴더 구조 (구글 드라이브 서버 → 로컬 폴더)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
. (root directory)
├── A
   ├── a
      └── .txt
   └── 1.txt
├── B
   ├── b
      └── .txt
   └── 2.txt
└── C
    ├── c
       └── .txt
    └── 3.txt

마찬가지로 동기화 폴더 설정 : 로컬 폴더 → 구글 드라이브 서버를 확인하기 위해 동기화 할 로컬 폴더를 아래와 같이 설정했다.

# 로컬 폴더 구조 (로컬 폴더 → 구글 드라이브 서버)

1
2
3
4
. (root directory)
└── bck
    └── backup.txt

구글 드라이브 서버 → 로컬 폴더는 기본 경로(%USERPROFILE%/Google 드라이브)로 설정했으며, 로컬 폴더 → 구글 드라이브 서버는 별도의 폴더(%USERPROFILE%/backup_folder)로 설정했다. 또한 저장매체 연결 흔적을 확인하기 위해 볼륨 이름이 “PLAINBIT”인 NTFS로 포맷 한 USB 메모리를 사용했다.

# 로그인 관련 흔적

설치 직후 레지스트리에는 클라이언트 버전과 설정 파일의 위치와 관련된 흔적이 다음의 경로에 남는다.
- NTUSER.DAT/Software/Google/Drive

이때 로그인을 진행하면 위의 레지스트리 키 하위에 다음과 같은 형태의 Value 가 생성된다.

NTUSER.DAT/Software/Google/Drive 흔적

위의 레지스트리 Value 중 로그인 정보와 관련 있는 것은 다음과 같다.
- "machine_{Some_String_1}" = dword:"{Some_String_2}"
- "OAuthToken_{Some_String_1}" = dword:"{Some_String_2}"

[Some_String_1] 은 운영체제가 다르더라도 같은 계정으로 로그인 했다면 레지스트리에 동일하게 생성되며, [Some_String_2] 는 서로 다른 운영체제에 같은 계정을 사용한 경우 앞에서 부터 일부만 같으며 그 이후는 다르다. 이 문자열을 활용하면 서로 다른 호스트에서 발견한 구글 드라이브 계정의 동일성을 비교할 수 있다.

특히 “OAuthToken_” 을 Prefix로 갖는 레지스트리 Value의 Data는 Base64로 인코딩 값이다. 이 값을 디코딩하면 16진수로 다음과 같은 형태를 확인할 수 있다.

NTUSER.DAT/Software/Google/Drive:OAuthToken 키 데이터 Base64 디코딩 결과 일부

위의 그림에서 음영 처리된 부분의 16진수 값(’01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB’)은 Windows DPAPI 를 활용해 Encrypt 한 값이 가지는 시그니처이다.

Windows DPAPI란 Windows 운영체제 레벨에서 사용하는 대칭키 암호 방식으로, 사용자의 로그온 증명을 활용하기 때문에 일반적으로 Windows 운영체제에 로그인이 된 라이브 상태에서만 복호화를 수행할 수 있다.

위 레지스트리 Data를 실험 환경에서 DPAPI 로 복호화 하면(관련도구) 다음과 같이 OAuthToken 과 관련 있는 데이터를 확인할 수 있다. OAuthToken 값은 호스트가 다르더라도 계정이 같다면 같은 토큰값을 사용한다.

OAuthToken 복호화 성공 시 결과 화면

레지스트리에 남는 흔적은 결과적으로, 해당 PC에서는 구글 드라이브 클라이언트로 로그인한 사실을 알 수 있다. 또한 서로 다른 PC에 설치되어 있는 구글 드라이브 클라이언트가 같은 계정으로 로그인 되었다는 사실의 근거로 활용할 수 있다.

Google 로그인 계정과 관련된 직접적인 정보는 다음의 SQLite 파일에서 확인할 수 있다.
- %UserProfile%\AppData\Local\Google\Drive\global.db

해당 SQLite DB 파일에서 global_preference 테이블의 preference_value 컬럼에 다음과 같은 형태로 저장된다.

계정 정보가 저장되는 global.db 파일

그밖에 다음 경로에 있는 SQLite 파일에도 계정 관련 흔적이 남는다.
- %UserProfile%\AppData\Local\Google\Drive\user_default\sync_config.db

sync_config.db 에는 data 테이블 하나만 존재한다. 이 테이블에서 계정 정보와 관련된 내용은 다음과 같다. entry_key 는 테이블의 컬럼명이다.

sync_config.db - data 테이블의 계정 관련 정보

따라서 위의 두 SQLite DB 를 통해 구글 드라이브 클라이언트에 어떤 계정으로 로그인했는지 알 수 있다.

# 동기화 폴더 내 파일 및 폴더 관련 흔적

위에서 확인한 sync_config.dbdata 테이블은 구글 드라이브 클라이언트가 활용하는 설정값들이 저장되어 있다. 이 설정 중에서 동기화 폴더와 관련된 흔적은 다음과 같다.

sync_config.db - data 테이블의 동기화 폴더 관련 정보

동기화 폴더 내 파일 및 폴더와 관련하여 더 상세한 흔적은 다음의 SQLite 파일에서 확인할 수 있다.
- %UserProfile%\AppData\Local\Google\Drive\user_default\snapshot.db

snapshot.dbcloud_entry, cloud_relations, local_entry, local_relations, mapping, pre_mapping, voloume_info 등의 테이블을 포함하고 있다.

cloud_entry, cloud_relations 테이블은 구글 클라우드 서버 내에 있는 파일 및 폴더 관련 정보를 저장한다. cloud_entry 테이블은 구글 드라이브 서버 내에 있는 파일 및 폴더 목록과 메타데이터를 저장하며, cloud_relations 테이블을 통해 서버 내에 저장되어 있는 파일 및 폴더의 경로 정보를 확인할 수 있다. 각 테이블의 컬럼 정보는 아래의 표를 참고하자.

snapshot.db - cloud_entry 테이블의 서버 내 파일/폴더 흔적
snapsot.db - cloud_relations 테이블의 서버 내 파일/폴더 포함 관계(경로)

local_entry, local_relations 테이블은 로컬로 동기화 된 항목들에 대한 정보를 저장하고 있다. local_entry 테이블은 로컬에 동기화 된 파일 및 폴더 목록과 메타데이터를 저장하며, local_relations 테이블을 통해 로컬로 동기화 된 파일 및 폴더의 경로 정보를 확인할 수 있다. 각 테이블의 컬럼 정보는 아래의 표를 참고하자.

snapshot.db - local_entry 테이블의 동기화 폴더 내 파일/폴더 흔적
snapshot.db - local_relations 테이블의 동기화 폴더 내 파일/폴더의 포함 관계 (경로)

mapping 테이블은 구글 드라이브 서버에 저장된 파일과 로컬 동기화 폴더에 저장된 파일간의 연결 정보를 저장한다. 즉, 테이블의 관계로 본다면 cloud_relationlocal_relation 테이블이 서로 연결된 형태이다. 자세한 테이블의 정보는 다음과 같다.

snapshot.db - mapping 테이블의 동기화 폴더에 저장된 파일/폴더의 매핑 정보

이상의 여러 테이블에 남는 흔적은 동기화 후 구글 드라이브 서버와 로컬 각각에 동기화 된 파일 및 폴더의 목록이므로, 서버로 부터 다운로드 받은 파일 및 폴더 혹은 서버로 업로드 된 파일 및 폴더의 정보를 알 수 있다.

또한 전체 경로 경로와 동기화 된 파일의 MD5 해시값도 확인할 수 있어 원본 파일이 없더라도 추후 확보 된다면 원본성을 비교할 수 있다. 하지만 로컬에서 업로드 된 파일인지 혹은 로컬로 다운로드 된 파일인지는 알 수 없다. 그리고 파일이 지워진 경우 테이블의 레코드에서도 삭제되기 때문에 흔적을 분석하는데 다소 한계가 존재한다.

# 로컬 볼륨 관련 흔적

한편 로컬 동기화 폴더가 속하는 볼륨에 대한 정보를 알 수 있다. snapshot.dbvolume_info 테이블은 다음과 같은 정보를 저장한다.

snapshot.db - volume_info 테이블의 볼륨 정보

volume_info 테이블의 정보는 동기화 폴더가 서로 다른 볼륨에 존재 할 경우 이를 구분하는 데에 활용할 수 있다. local_entry 테이블의 volume 컬럼에 남는 볼륨 시리얼 번호와 비교하면 가능하다.

# 이동식 저장매체 흔적

앞선 포스팅에서 구글 드라이브의 기능 중 이동식 저장 매체 백업 기능에 대해 확인했다. 이 기능은 옵션으로 켜고 끌 수 있는데, 위의 sync_config.dbdata 테이블을 확인하면 다음과 같은 설정으로 켜고 끄는 것을 확인할 수 있다.

sync_config.db - data 테이블의 저장매체 백업 옵션

이동식 저장매체 연결 흔적에 대한 상세한 정보는 다음의 SQLite 파일을 통해 확인할 수 있다.
- %UserProfile%\AppData\Local\Google\Drive\user_default\device_db.db

device_db.dbdevice_files, device_upload_info, external_devices, pending_files 테이블이 존재한다. 이 중 흔적을 확인할 수 있는 것은 device_filesexternal_devices 두 테이블이다.

구글 드라이브 사용 중 이동식 저장매체를 연결하게 되면, 매체에 저장된 파일에 대해 백업 기능을 사용할지 묻는다. 이때 백업을 활성화하면 구글 드라이브 서버로 저장매체 내 파일 및 폴더가 업로드 된다. device_files 테이블은 저장매체 내 파일 및 폴더가 백업되는 경우 그 흔적을 확인할 수 있다.

device_db.db - device_files 테이블의 파일/폴더 백업 관련 흔적

device_files 테이블은 저장매체의 백업 기능을 활성화 해야만 흔적 확인이 가능하다. 이와 다르게 external_devices 테이블은 저장매체가 연결이 되기만 해도 그 흔적이 남는다. 테이블에 남는 자세한 흔적은 다음과 같다.

device_db.db - external_devices 테이블의 이동식 저장매체 연결 흔적

# 동기화 관련 흔적

동기화 관련 흔적은 구글 드라이브 서버와 로컬 내 폴더 간 동기화 시 업로드 및 다운로드가 수행된다. 이 내역을 로그로 저장한 파일이 sync_log.log 이다.

sync_log.log 로그 파일이 저장되는 경로는 다음과 같다.
- %UserProfile%\AppData\Local\Google\Drive\user_default\sync_log.log

위의 경로의 로그는 문자열 형태로 저장되며, 텍스트 에디터 등을 통해 확인해보면 아래와 같은 형태의 로그를 살펴 볼 수 있다.

sync_log.log 파일의 일부

아래의 실험은 이 파일에 남는 로그의 형태를 케이스 별로 파악하기 위해 여러 상황을 가정하고 진행하였다. 로그로 기록되는 내용은 실시간으로 확인하기 위해 tail -f 명령어를 사용하였다.

# 파일 업로드 관련 흔적

로컬에 파일이 생성되는 경우 다음과 같은 형태의 로그를 확인할 수 있다.

1. 파일시스템 내 변화 감지

1
2
3
4
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Starting primary scan of xx paths.
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Dirty: xx Scan: xx Cor: xx  ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.UPLOAD, Acion.CREATE, ..., path='{생성된 파일의 경로}'), ...)

2. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.UPLOAD, Acion.CREATE, ..., path='{생성된 파일이 위치한 폴더}', name='{생성된 파일이름}'), ...)]

3. worker – 서버로 업로드 요청

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: InsertFile(... filename='{생성된 파일의 경로}'..., doc_id='{생성된 파일의 doc_id}') ... {업로드 계정}

4. worker – 서버로 업로드 결과

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive Response: File(..., md5_checksum='{생성된 파일의 MD5 해쉬값}', ... , doc_id='{생성된 파일의 doc_id}', ...)

5. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.UPLOAD, Action.CREATE, ..., path='{생성된 파일이 위치한 폴더}', name='{생성된 파일이름}', ...)

즉, 동작이 종료되는 시점인 다섯번째 형태의 로그가 남는다면, 로컬에서 생성된 파일이 구글 드라이브 서버로 업로드 된 흔적이라고 판단 가능하다.

# 로컬의 파일 열람 및 수정 후 저장하는 경우

로컬 파일을 열람하는 경우에는 별도의 로그를 남기지 않지만, 수정 후 저장하는 경우에는 로그를 생성한다. 로그의 형태와 흐름은 파일을 생성하는 경우와 거의 유사하며 주로 로그에서 정의하는 Action 항목이 Action.CREATE 에서 Action.MODIFY 로 바뀐다.

1. 파일시스템 내 변화 감지

1
2
3
4
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Starting primary scan of xx paths.
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Dirty: xx Scan: xx Cor: xx ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.UPLOAD, Acion.MODIFY, ..., path='{수정된 파일의 경로}'), ...)

2. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.UPLOAD, Acion.MODIFY, ..., path='{수정된 파일이 위치한 폴더}', name='{수정된 파일이름}'), ...)]

3. worker – 서버로 업로드 요청

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: UpdateFile(... filename='{수정된 파일의 경로}'..., doc_id='{수정된 파일의 doc_id}') ... {업로드 계정}

4. worker – 서버로 업로드 결과

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive Response: ..., md5_checksum='{수정된 파일의 MD5 해쉬값}', ... , doc_id='{수정된 파일의 doc_id}', ...

5. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.UPLOAD, Action.MODIFY, ..., path='{수정된 파일이 위치한 폴더}', name='{수정된 파일이름}', ...)

# 파일 다운로드 관련 흔적

파일 다운로드와 관련된 로그는 크게 드라이브 서버의 변화, 파일시스템에 반영 두 부분으로 나눌 수 있다.

먼저, 로컬에 존재하지 않는 파일이 서버에 생성되는 경우 다음과 같은 형태의 로그를 확인할 수 있다.

1. 서버 내 변화 감지 및 변화 요인 파악

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... cloud_watcher.py ... Getting changes; ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... drive - '{구글 드라이브 계정}' - BatchRequest with x inner requiests: ListChanges(..., tags=Reason.SYNC_PUSHED_CHANGES, ...) ...

2. 서버로 변화된 항목 요청

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... discovery.py ... URL being requests: GET ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... Drive batch completed: ...

3. 파일시스템 변화 감지

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.DOWNLOAD, Acion.CREATE, ..., doc_id='{파일의 doc_id}'), checksum={파일의 MD5 해시}, mapped={다운로드 파일이 위치할 경로}...)

4. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.DOWNLOAD, Acion.CREATE, ..., path='{생성된 파일이 위치할 폴더}', name='{생성된 파일이름}'), ...)]

5. worker – 서버로 다운로드 요청

1
2
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: DownloadBlob(... filepath='{로컬에 임시로 저장할 파일의 경로}'..., doc_id='{파일의 doc_id}') ... {다운로드 계정}
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... downloads.py ... Downloading {구글 API 서버 URL} to {로컬에 임시로 저장할 파일 경로}

임시 저장 폴더는 구글드라이브 서버 → 로컬 폴더의 기본 경로(%USERPROFILE%/Google 드라이브) 의 .tmp.drivedownload 폴더이다.

6. worker – 서버로 부터 다운로드 결과

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive Response: ..., md5_checksum='{생성된 파일의 MD5 해쉬값}', ... , doc_id='{생성된 파일의 doc_id}', ...

7. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.DOWNLOAD, Action.CREATE, ..., path='{생성된 파일이 위치한 폴더}', name='{생성된 파일이름}', ...)

다음으로 로컬에 존재하는 파일이 서버에서 수정 된 경우 다음과 같은 형태의 로그를 확인할 수 있다.

1. 서버 내 변화 감지 및 변화 요인 파악

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... cloud_watcher.py ... Getting changes; ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... drive - '{구글 드라이브 계정}' - BatchRequest with x inner requiests: ListChanges(..., tags=Reason.SYNC_PUSHED_CHANGES, ...) ...

2. 서버로 변화된 항목 요청

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... discovery.py ... URL being requests: GET ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... Drive batch completed: ...

3. 파일시스템 변화 감지

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.DOWNLOAD, Acion.MODIFY, ..., mapped={수정된 파일이 위치할 경로}, ..., doc_id='{파일의 doc_id}'), , ...)

4. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.DOWNLOAD, Acion.MODIFY, ..., path='{수정된 파일이 위치할 폴더}', name='{수정된 파일이름}'), ...)]

5. worker – 서버로 다운로드 요청

1
2
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: DownloadBlob(... filepath='{로컬에 임시로 저장할 파일의 경로}'..., doc_id='{파일의 doc_id}') ... {다운로드 계정}
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... downloads.py ... Worker statring on [FSChangeownloading {구글 API 서버 URL} to {로컬에 임시로 저장할 파일 경로}]

6. worker – 서버로 부터 다운로드 결과

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive Response: ..., md5_checksum='{수정된 파일의 MD5 해쉬값}', ... , doc_id='{파일의 doc_id}', ...

파일이 수정되더라도 처음에 부여받은 doc_id는 그대로 유지된다.

7. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.DOWNLOAD, Action.MODIFY, ..., path='{수정된 파일이 위치한 폴더}', name='{수정된 파일이름}', ...)

# 파일 삭제 관련 흔적

로컬에 존재하지 하는 파일이 서버에 삭제된 경우 다음과 같은 형태의 로그를 확인할 수 있다.

1. 서버 내 변화 감지 및 변화 요인 파악

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... cloud_watcher.py ... Getting changes; ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... drive - '{구글 드라이브 계정}' - BatchRequest with x inner requiests: ListChanges(..., tags=Reason.SYNC_PUSHED_CHANGES, ...) ...

2. 서버로 변화된 항목 요청

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... discovery.py ... URL being requests: GET ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_batch.py ... Drive batch completed: ...

3. 파일시스템 변화 감지

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.DOWNLOAD, Acion.DELETE, ..., mapped={삭제할 파일이 위치한 경로}, ..., doc_id='{삭제할 파일의 doc_id}'), ...)

4. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.DOWNLOAD, Acion.DELETE, ..., mapped='{삭제할 파일이 위치한 폴더}', doc_id='{삭제할 파일의 doc_id}', ...)]

5. worker – 파일 삭제

1
2
3
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: GetItem(tags=Reason.DELETE_OBJECT, ... , doc_id='{삭제할 파일의 doc_id}', ...) ... {구글 계정}
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... downloads.py ... Using path {로컬에 삭제 파일 경로} for download delete of local_id ... doc_id={삭제할 파일 경로}

6. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.DOWNLOAD, Action.DELETE, ..., mapped={삭제한 파일이 위치했던 경로}, ...)

로컬에 존재하는 파일이 삭제된 경우 다음과 같은 형태의 로그를 확인할 수 있다.

1. 파일시스템 내 변화 감지

1
2
3
4
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Starting primary scan of xx paths.
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... tree_scanner.py ... Dirty: xx Scan: xx Cor: xx ...
...
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... aggregator.py ... Received change FSChange(Direction.UPLOAD, Acion.DELETE, ..., path='{삭제할 파일이 위치한 폴더}', name='{삭제할 파일의 이름}' ...)

2. worker – 동작 수행 시작

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker starting on [FSChange(Direction.UPLOAD, Acion.DELETE, ..., path='{삭제할 파일이 위치한 폴더}', name='{삭제할 파일의 이름}', ...)]

3. worker – 파일 삭제

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... drive_request.py ... Drive request: TrashItem(..., tags=Reason.TRASH_OBJECT, ... , doc_id='{삭제할 파일의 doc_id}', ...) ... {구글 계정}

4. worker – 동작 종료

1
yyyy-MM-dd hh:mm:ss +(timezoone) INFO ... worker.py ... Worker successfully completed [FSChange(Direction.UPLOAD, Action.DELETE, ..., path='{삭제한 파일이 위치했던 경로}', name='{삭제한 파일의 이름}'...)

# sync_log.log 분석 정리

로그 분석을 통해 동기화 기능은 worker 를 중심으로 운영되는 것을 알 수 있다. 즉, sync_log.log 는 worker 동작이 종료되는 것을 중점적으로 분석해야 한다. 특히 종료 시점에 남는 FSChange 항목을 정리하면 다음과 같다.

FSChange 옵션과 관련된 행위

# 맺음글

본 포스팅에서 다룬 실험 결과와 같이, Windows 호스트 에서 구글 드라이브 클라이언트를 사용했다면 포렌식 분석가 관점에서 의미있는 흔적을 발견할 수 있다. 계정 로그인과 관련하여 로그인 토큰과 구글 계정에 대한 흔적이 남으며, 이를 통해 다른 시스템에서 같은 계정으로 로그인이 되었는지, 어떤 계정으로 로그인이 이루어졌는지 비교 가능하다.

또한 구글 드라이브 서버와 클라이언트에 동기화 된 파일 및 폴더에 대해 목록화할 수 있으며, 이는 동기화 로그와 연관 분석하여 클라이언트를 통해 업로드 및 다운로드 된 파일에 대해 확인할 수 있다. 이동식 저장매체의 경우 연결흔적이 남는 한편, 조건이 갖춰진 경우 저장매체 내 존재 했던 파일에 대한 목록 또한 확보할 수 있다. 분석가는 이를 종합적으로 포렌식 관점에서의 정보 유출 가능성을 고려해야 한다.

구글 드라이브는 위에서 밝힌 기능 이외에도 여러 계정 간 동시 수정, 계정 및 URL 공유 등의 기능을 활용 가능하므로 본 글에서 다루지 못한 케이스가 존재한다. 이후에는 이에 대한 지속적인 연구가 필요하다.

실험의 결과를 바탕으로 개발된 분석도구는 링크를 통해 공개 할 예정이다. 분석에 도움이 되길 바란다.