Python 디지털 포렌식-빠른 가이드

이 장에서는 디지털 포렌식이 무엇인지에 대한 소개와 역사적 검토를 제공합니다. 또한 실생활에서 디지털 포렌식을 적용 할 수있는 위치와 그 한계를 이해하게됩니다.

디지털 포렌식이란 무엇입니까?

디지털 포렌식은 전자 장치에있는 디지털 증거를 분석, 조사, 식별 및 복구하는 포렌식 과학의 한 분야로 정의 될 수 있습니다. 일반적으로 형법 및 개인 수사에 사용됩니다.

예를 들어, 누군가가 전자 장치에서 일부 데이터를 훔치는 경우 디지털 포렌식 추출 증거에 의존 할 수 있습니다.

디지털 포렌식의 간략한 역사적 검토

이 섹션에서는 컴퓨터 범죄의 역사와 디지털 포렌식의 역사 검토에 대해 설명합니다.

1970 ~ 1980 년대 : 최초의 컴퓨터 범죄

이 10 년 전에는 컴퓨터 범죄가 인정되지 않았습니다. 그러나 그것이 일어날 것으로 예상된다면 당시의 기존 법률이 그들을 다루었습니다. 나중에 1978 년 플로리다 컴퓨터 범죄 법 (Florida Computer Crime Act)에서 컴퓨터 시스템에있는 데이터의 무단 수정 또는 삭제에 대한 법률이 포함 된 최초의 컴퓨터 범죄가 인정되었습니다. 그러나 시간이 지남에 따라 기술의 발전으로 인해 컴퓨터 범죄의 범위도 증가했습니다. 저작권, 사생활, 아동 포르노와 관련된 범죄를 처리하기 위해 다양한 법률이 통과되었습니다.

1980 년대 -1990 년대 : 개발 10 년

이 10 년은 Cliff Stoll이 Markus Hess라는 해커를 추적 한 최초의 조사 (1986) 덕분에 디지털 포렌식의 개발 10 년이었습니다. 이 기간 동안 두 가지 종류의 디지털 포렌식 분야가 개발되었습니다. 첫 번째는이를 취미로 삼은 실무자들이 개발 한 임시 도구와 기술의 도움으로, 두 번째는 과학 커뮤니티에서 개발했습니다. 1992 년에“Computer Forensics”학술 문헌에 사용되었습니다.

2000 년대 -2010 년대 : 10 년의 표준화

디지털 포렌식이 일정 수준으로 발전한 후 조사를 수행하는 동안 따를 수있는 특정 표준을 만들 필요가있었습니다. 이에 따라 다양한 과학 기관과 기관이 디지털 포렌식에 대한 지침을 발표했습니다. 2002 년에 SWGDE (Scientific Working Group on Digital Evidence)는 "컴퓨터 포렌식을위한 모범 사례"라는 논문을 발표했습니다. 모자의 또 다른 깃털은 유럽 주도의 국제 조약입니다.“The Convention on Cybercrime”43 개국이 서명하고 16 개국이 비준했다. 이러한 표준 이후에도 연구원들이 확인한 몇 가지 문제를 해결해야 할 필요가 있습니다.

디지털 포렌식 프로세스

1978 년 최초의 컴퓨터 범죄 이후 디지털 범죄 활동이 엄청나게 증가했습니다. 이러한 증가로 인해이를 처리하기위한 구조화 된 방식이 필요합니다. 1984 년에 공식화 된 프로세스가 도입되었으며 그 이후 많은 새롭고 개선 된 컴퓨터 포렌식 조사 프로세스가 개발되었습니다.

컴퓨터 법의학 조사 프로세스는 아래에 설명 된대로 세 가지 주요 단계를 포함합니다.

1 단계 : 전시회 수집 또는 이미징

디지털 포렌식의 첫 번째 단계는 나중에 분석 할 수 있도록 디지털 시스템의 상태를 저장하는 것입니다. 범죄 현장에서 사진, 혈액 샘플 등을 찍는 것과 매우 유사합니다. 예를 들어, 하드 디스크 또는 RAM의 할당 된 영역과 할당되지 않은 영역의 이미지 캡처가 포함됩니다.

2 단계 : 분석

이 단계의 입력은 수집 단계에서 수집 된 데이터입니다. 여기에서 증거를 확인하기 위해이 데이터를 조사했습니다. 이 단계는 다음과 같은 세 가지 증거를 제공합니다.

  • Inculpatory evidences − 이러한 증거는 주어진 역사를 뒷받침합니다.

  • Exculpatory evidences − 이러한 증거는 주어진 역사와 모순됩니다.

  • Evidence of tampering− 이러한 증거는 시스템이 식별을 피하기 위해 조정되었음을 보여줍니다. 여기에는 삭제 된 파일을 복구하기위한 파일 및 디렉터리 내용 검사가 포함됩니다.

3 단계 : 프레젠테이션 또는보고

이름에서 알 수 있듯이이 단계는 조사의 결론과 해당 증거를 제시합니다.

디지털 포렌식의 응용

디지털 포렌식은 디지털 장치에 포함 된 증거를 수집, 분석 및 보존하는 것을 다룹니다. 디지털 포렌식의 사용은 애플리케이션에 따라 다릅니다. 앞서 언급했듯이 주로 다음 두 가지 응용 프로그램에서 사용됩니다.

형법

형법에서는 법원에서 가설을지지하거나 반대하기 위해 증거를 수집합니다. 법의학 절차는 범죄 수사에 사용되는 절차와 매우 유사하지만 법적 요건과 제한 사항이 다릅니다.

개인 조사

주로 기업 세계에서는 개인 조사를 위해 디지털 포렌식을 사용합니다. 회사에서 직원이 회사 정책에 위배되는 불법 행위를 컴퓨터에서 수행하고 있다고 의심 될 때 사용됩니다. 디지털 포렌식은 회사 또는 개인이 디지털 부정 행위를 조사 할 때 취할 수있는 가장 좋은 방법 중 하나를 제공합니다.

디지털 포렌식의 지점

디지털 범죄는 컴퓨터에만 국한되지 않지만 해커와 범죄자들도 태블릿, 스마트 폰 등과 같은 소형 디지털 장치를 대규모로 사용하고 있습니다. 일부 장치에는 휘발성 메모리가 있고 다른 장치에는 비 휘발성 메모리가 있습니다. 따라서 장치 유형에 따라 디지털 포렌식에는 다음과 같은 분기가 있습니다.

컴퓨터 법의학

이 디지털 포렌식 분야는 컴퓨터, 임베디드 시스템 및 USB 드라이브와 같은 정적 메모리를 다룹니다. 로그에서 드라이브의 실제 파일에 이르기까지 다양한 정보를 컴퓨터 포렌식에서 조사 할 수 있습니다.

모바일 포렌식

여기에서는 모바일 장치의 데이터 조사를 다룹니다. 이 지점은 모바일 장치에 위치와 관련된 유용한 정보를 제공하는 데 유용한 통신 시스템이 내장되어 있다는 점에서 컴퓨터 포렌식과 다릅니다.

네트워크 포렌식

정보 수집, 증거 수집 또는 침입 탐지를 위해 로컬 및 WAN (광역 네트워크)의 컴퓨터 네트워크 트래픽 모니터링 및 분석을 다룹니다.

데이터베이스 포렌식

이 디지털 포렌식 분야는 데이터베이스 및 메타 데이터에 대한 포렌식 연구를 다룹니다.

디지털 포렌식 조사에 필요한 기술

디지털 포렌식 검사관은 해커를 추적하고, 훔친 데이터를 복구하고, 컴퓨터 공격을 소스로 되돌리고, 컴퓨터와 관련된 다른 유형의 조사를 지원합니다. 아래에 설명 된대로 디지털 포렌식 심사관이되기 위해 필요한 핵심 기술 중 일부-

뛰어난 사고 능력

디지털 포렌식 조사자는 뛰어난 사상가 여야하며 산출물을 얻기위한 특정 과제에 대해 다양한 도구와 방법론을 적용 할 수 있어야합니다. 서로 다른 패턴을 찾아서 상관 관계를 만들 수 있어야합니다.

기술 능력

디지털 포렌식 심사관은이 분야에서 네트워크에 대한 지식과 디지털 시스템이 상호 작용하는 방식에 대한 지식이 필요하기 때문에 우수한 기술 능력이 있어야합니다.

사이버 보안에 대한 열정

디지털 포렌식 분야는 모두 사이버 범죄 해결에 관한 것이고 이것은 지루한 작업이기 때문에 누군가가 에이스 디지털 포렌식 수사관이 되려면 많은 열정이 필요합니다.

의사 소통 능력

다양한 팀과 협력하고 누락 된 데이터 또는 정보를 추출하려면 좋은 의사 소통 기술이 필수입니다.

보고서 작성에 능숙

획득 및 분석을 성공적으로 구현 한 후 디지털 포렌식 검사관은 최종 보고서 및 프레젠테이션에 모든 결과를 언급해야합니다. 따라서 그는 보고서 작성에 능숙하고 세부 사항에주의를 기울여야합니다.

한계

디지털 포렌식 조사는 여기에 설명 된대로 특정 제한을 제공합니다.

설득력있는 증거를 제시해야 함

디지털 포렌식 조사의 주요 단점 중 하나는 데이터가 쉽게 변조 될 수 있기 때문에 검사관이 법정에서 증거에 필요한 표준을 준수해야한다는 것입니다. 반면에 컴퓨터 법의학 수사관은 법정에서 설득력있는 증거를 제시하기 위해 법적 요건, 증거 처리 및 문서화 절차에 대한 완전한 지식을 가지고 있어야합니다.

조사 도구

디지털 조사의 효과는 전적으로 디지털 포렌식 심사관의 전문성과 적절한 조사 도구의 선택에 달려 있습니다. 사용 된 도구가 지정된 기준에 맞지 않으면 법정에서 판사가 증거를 거부 할 수 있습니다.

청중 사이의 기술 지식 부족

또 다른 제한은 일부 개인이 컴퓨터 포렌식에 완전히 익숙하지 않다는 것입니다. 따라서 많은 사람들이이 분야를 이해하지 못합니다. 수사관은 모든 사람이 결과를 이해하는 데 도움이되는 방식으로 조사 결과를 법원에 전달해야합니다.

비용

디지털 증거를 생성하고 보존하는 데는 많은 비용이 듭니다. 따라서 비용을 감당할 수없는 많은 사람들이이 프로세스를 선택하지 못할 수 있습니다.

이전 장에서 우리는 디지털 포렌식의 기본, 장점 및 한계를 배웠습니다. 이 장에서는 디지털 포렌식 조사에서 사용하는 필수 도구 인 Python에 익숙해 질 것입니다.

왜 디지털 포렌식을위한 파이썬인가?

Python은 널리 사용되는 프로그래밍 언어이며 사이버 보안, 침투 테스트 및 디지털 포렌식 조사 도구로 사용됩니다. 디지털 포렌식 도구로 Python을 선택하면 작업을 완료하는 데 다른 타사 소프트웨어가 필요하지 않습니다.

디지털 포렌식 프로젝트에 적합한 Python 프로그래밍 언어의 고유 한 기능 중 일부는 다음과 같습니다.

  • Simplicity of Syntax − Python의 구문은 다른 언어에 비해 간단하기 때문에 디지털 포렌식을 배우고 사용하기가 더 쉽습니다.

  • Comprehensive inbuilt modules − Python의 포괄적 인 내장 모듈은 완전한 디지털 포렌식 조사를 수행하는 데 탁월한 도움이됩니다.

  • Help and Support − 오픈 소스 프로그래밍 언어 인 Python은 개발자 및 사용자 커뮤니티의 탁월한 지원을 받고 있습니다.

Python의 특징

Python은 높은 수준의 해석 된 대화 형 객체 지향 스크립팅 언어로서 다음과 같은 기능을 제공합니다.

  • Easy to Learn − Python은 키워드가 적고 구조가 가장 단순하기 때문에 개발자 친화적이고 배우기 쉬운 언어입니다.

  • Expressive and Easy to read− Python 언어는 본질적으로 표현 적입니다. 따라서 코드는 더 이해하기 쉽고 읽기 쉽습니다.

  • Cross-platform Compatible − Python은 크로스 플랫폼 호환 언어이므로 UNIX, Windows 및 Macintosh와 같은 다양한 플랫폼에서 효율적으로 실행할 수 있습니다.

  • Interactive Mode Programming − Python은 프로그래밍을위한 대화 형 모드를 지원하기 때문에 대화 형 테스트 및 코드 디버깅을 수행 할 수 있습니다.

  • Provides Various Modules and Functions − Python에는 스크립트에 대한 풍부한 모듈 및 함수 세트를 사용할 수있는 대규모 표준 라이브러리가 있습니다.

  • Supports Dynamic Type Checking − Python은 동적 유형 검사를 지원하고 매우 높은 수준의 동적 데이터 유형을 제공합니다.

  • GUI Programming − Python은 GUI 프로그래밍을 지원하여 그래픽 사용자 인터페이스를 개발합니다.

  • Integration with other programming languages − Python은 C, C ++, JAVA 등과 같은 다른 프로그래밍 언어와 쉽게 통합 될 수 있습니다.

Python 설치

Python 배포는 Windows, UNIX, Linux 및 Mac과 같은 다양한 플랫폼에서 사용할 수 있습니다. 플랫폼에 따라 바이너리 코드 만 다운로드하면됩니다. 플랫폼에 대한 바이너리 코드를 사용할 수없는 경우 소스 코드를 수동으로 컴파일 할 수 있도록 C 컴파일러가 있어야합니다.

이 섹션에서는 다양한 플랫폼에 Python을 설치하는 방법에 대해 설명합니다.

Unix 및 Linux에 Python 설치

아래 단계에 따라 Unix / Linux 컴퓨터에 Python을 설치할 수 있습니다.

Step 1− 웹 브라우저를 엽니 다. www.python.org/downloads/를 입력하고 입력합니다.

Step 2 − Unix / Linux에서 사용할 수있는 압축 된 소스 코드를 다운로드합니다.

Step 3 − 다운로드 한 압축 파일을 추출합니다.

Step 4 − 일부 옵션을 사용자 지정하려면 Modules/Setup file.

Step 5 − 다음 명령을 사용하여 설치 완료 −

run ./configure script
make
make install

위의 단계를 성공적으로 완료하면 Python이 표준 위치에 설치됩니다. /usr/local/bin 및 도서관 /usr/local/lib/pythonXX 여기서 XX는 Python 버전입니다.

Windows에 Python 설치

다음과 같은 간단한 단계에 따라 Windows 컴퓨터에 Python을 설치할 수 있습니다.

Step 1− 웹 브라우저를 엽니 다. www.python.org/downloads/를 입력하고 입력합니다.

Step 2 − Windows 설치 프로그램 다운로드 python-XYZ.msi 여기서 XYZ는 설치해야하는 버전입니다.

Step 3 − 이제 설치 프로그램 파일을 로컬 컴퓨터에 저장 한 후 해당 MSI 파일을 실행합니다.

Step 4 − 다운로드 한 파일을 실행하면 Python 설치 마법사가 나타납니다.

Macintosh에 Python 설치

Mac OS X에 Python 3을 설치하려면 다음과 같은 패키지 설치 프로그램을 사용해야합니다. Homebrew.

시스템에 Homebrew가없는 경우 다음 명령을 사용하여 Homebrew를 설치할 수 있습니다.

$ ruby -e "$(curl -fsSL
https://raw.githubusercontent.com/Homebrew/install/master/install)"

패키지 관리자를 업데이트해야하는 경우 다음 명령을 사용하여 수행 할 수 있습니다.

$ brew update

이제 다음 명령을 사용하여 시스템에 Python3을 설치하십시오.

$ brew install python3

PATH 설정

Python 설치 경로를 설정해야하며 이는 UNIX, WINDOWS 또는 MAC과 같은 플랫폼에 따라 다릅니다.

Unix / Linux에서 경로 설정

다음 옵션을 사용하여 Unix / Linux에서 경로를 설정할 수 있습니다.

  • If using csh shell -유형 setenv PATH "$PATH:/usr/local/bin/python" 그런 다음 Enter를 누릅니다.

  • If using bash shell (Linux) − 유형 export ATH="$PATH:/usr/local/bin/python" 그런 다음 Enter를 누릅니다.

  • If using sh or ksh shell -유형 PATH="$PATH:/usr/local/bin/python" 그런 다음 Enter를 누릅니다.

Windows에서 경로 설정

유형 path %path%;C:\Python 명령 프롬프트에서 Enter를 누르십시오.

Python 실행

다음 세 가지 방법 중 하나를 선택하여 Python 인터프리터를 시작할 수 있습니다.

방법 1 : 대화 형 통역사 사용

명령 줄 인터프리터 또는 셸을 제공하는 시스템은 Python을 시작하는 데 쉽게 사용할 수 있습니다. 예를 들어, Unix, DOS 등. 아래 단계에 따라 인터랙티브 인터프리터에서 코딩을 시작할 수 있습니다.

Step 1 − 입력 python 명령 줄에서.

Step 2 − 아래 표시된 명령을 사용하여 대화 형 인터프리터에서 즉시 코딩을 시작합니다. −

$python # Unix/Linux
or
python% # Unix/Linux
or
C:> python # Windows/DOS

방법 2 : 명령 줄에서 스크립트 사용

응용 프로그램에서 인터프리터를 호출하여 명령 줄에서 Python 스크립트를 실행할 수도 있습니다. 아래에 표시된 명령을 사용할 수 있습니다.

$python script.py # Unix/Linux
or
python% script.py # Unix/Linux
or
C: >python script.py # Windows/DOS

방법 3 : 통합 개발 환경

시스템에 Python을 지원하는 GUI 애플리케이션이있는 경우 해당 GUI 환경에서 Python을 실행할 수 있습니다. 다양한 플랫폼을위한 IDE 중 일부는 다음과 같습니다.

  • Unix IDE − UNIX에는 Python 용 IDLE IDE가 있습니다.

  • Windows IDE − Windows에는 GUI와 함께 Python 용 최초의 Windows 인터페이스 인 PythonWin이 있습니다.

  • Macintosh IDE − Macintosh에는 MacBinary 또는 BinHex'd 파일로 다운로드 할 수있는 메인 웹 사이트에서 구할 수있는 IDLE IDE가 있습니다.

이제 로컬 시스템에 Python 명령을 설치하고 실행하는 데 익숙해 졌으므로 포렌식 개념에 대해 자세히 살펴 보겠습니다. 이 장에서는 Python 디지털 포렌식에서 아티팩트를 처리하는 데 관련된 다양한 개념을 설명합니다.

보고서 작성의 필요성

디지털 포렌식 프로세스에는 세 번째 단계로보고가 포함됩니다. 이것은 디지털 포렌식 프로세스에서 가장 중요한 부분 중 하나입니다. 다음과 같은 이유로 보고서 작성이 필요합니다.

  • 디지털 포렌식 심사관이 조사 프로세스와 그 결과를 설명하는 문서입니다.

  • 좋은 디지털 포렌식 보고서는 주어진 동일한 저장소에서 동일한 결과를 얻기 위해 다른 검사관이 참조 할 수 있습니다.

  • 디지털 증거의 1과 0에서 발견 된 사실을 포함하는 기술 및 과학 문서입니다.

보고서 작성을위한 일반 지침

보고서는 독자에게 정보를 제공하기 위해 작성되었으며 견고한 기초에서 시작해야합니다. 조사자는 일반적인 지침이나 표준없이 보고서를 작성하는 경우 조사 결과를 효율적으로 제시하는 데 어려움을 겪을 수 있습니다. 디지털 포렌식 보고서를 작성하는 동안 따라야하는 몇 가지 일반적인 지침은 다음과 같습니다.

  • Summary − 보고서에는 독자가 보고서의 목적을 확인할 수 있도록 간략한 정보 요약이 포함되어야합니다.

  • Tools used − 목적을 포함하여 디지털 포렌식 프로세스를 수행하는 데 사용 된 도구를 언급해야합니다.

  • Repository − 누군가의 컴퓨터를 조사한 다음 이메일, 내부 검색 기록 등과 같은 관련 자료의 증거 요약 및 분석을 조사한 다음 사례가 명확하게 제시 될 수 있도록 보고서에 포함되어야한다고 가정합니다.

  • Recommendations for counsel − 보고서에는 보고서의 결과를 기반으로 조사를 계속하거나 중단 할 수있는 조언이 있어야합니다.

다양한 유형의 보고서 생성

위 섹션에서 우리는 디지털 포렌식에서 보고서를 작성하기위한 지침과 함께 보고서의 중요성에 대해 알게되었습니다. 다른 종류의 보고서를 생성하기위한 Python의 일부 형식은 아래에서 설명합니다.

CSV 보고서

보고서의 가장 일반적인 출력 형식 중 하나는 CSV 스프레드 시트 보고서입니다. 다음과 같이 Python 코드를 사용하여 처리 된 데이터의 보고서를 생성하기 위해 CSV를 생성 할 수 있습니다.

먼저 스프레드 시트 작성에 유용한 라이브러리를 가져옵니다.

from __future__ import print_function
import csv
import os
import sys

이제 다음 메서드를 호출하십시오.

Write_csv(TEST_DATA_LIST, ["Name", "Age", "City", "Job description"], os.getcwd())

샘플 데이터 유형을 나타 내기 위해 다음 전역 변수를 사용하고 있습니다.

TEST_DATA_LIST = [["Ram", 32, Bhopal, Manager], 
   ["Raman", 42, Indore, Engg.],
   ["Mohan", 25, Chandigarh, HR], 
   ["Parkash", 45, Delhi, IT]]

다음으로 추가 작업을 진행하는 방법을 정의하겠습니다. "w"모드에서 파일을 열고 newline 키워드 인수를 빈 문자열로 설정합니다.

def Write_csv(data, header, output_directory, name = None):
   if name is None:
      name = "report1.csv"
   print("[+] Writing {} to {}".format(name, output_directory))
   
   with open(os.path.join(output_directory, name), "w", newline = "") as \ csvfile:
      writer = csv.writer(csvfile)
      writer.writerow(header)
      writer.writerow(data)

위의 스크립트를 실행하면 report1.csv 파일에 다음과 같은 세부 정보가 저장됩니다.

이름 나이 시티 지정
32 보팔 관리자
라만 42 인도 르 Engg
모한 25 찬디 가르 HR
파카 쉬 45 델리 그것

Excel 보고서

보고서의 또 다른 일반적인 출력 형식은 Excel (.xlsx) 스프레드 시트 보고서입니다. Excel을 사용하여 표를 만들고 그래프를 그릴 수도 있습니다. 아래와 같이 Python 코드를 사용하여 Excel 형식으로 처리 된 데이터의 보고서를 생성 할 수 있습니다

먼저 스프레드 시트를 만들기 위해 XlsxWriter 모듈을 가져옵니다.

import xlsxwriter

이제 통합 문서 개체를 만듭니다. 이를 위해 Workbook () 생성자를 사용해야합니다.

workbook = xlsxwriter.Workbook('report2.xlsx')

이제 add_worksheet () 모듈을 사용하여 새 워크 시트를 만듭니다.

worksheet = workbook.add_worksheet()

다음으로 워크 시트에 다음 데이터를 씁니다.

report2 = (['Ram', 32, ‘Bhopal’],['Mohan',25, ‘Chandigarh’] ,['Parkash',45, ‘Delhi’])

row = 0
col = 0

이 데이터를 반복하고 다음과 같이 쓸 수 있습니다.

for item, cost in (a):
   worksheet.write(row, col, item)
   worksheet.write(row, col+1, cost)
   row + = 1

이제 close () 메서드를 사용하여이 Excel 파일을 닫습니다.

workbook.close()

위의 스크립트는 다음 데이터를 갖는 report2.xlsx라는 이름의 Excel 파일을 생성합니다.

32 보팔
모한 25 찬디 가르
파카 쉬 45 델리

조사 수집 미디어

조사자는 조사 결과를 정확하게 기억하거나 조사의 모든 부분을 모으기 위해 상세한 조사 기록을 갖는 것이 중요합니다. 스크린 샷은 특정 조사를 위해 취한 단계를 추적하는 데 매우 유용합니다. 다음 Python 코드의 도움으로 스크린 샷을 찍고 나중에 사용할 수 있도록 하드 디스크에 저장할 수 있습니다.

먼저 다음 명령을 사용하여 pyscreenshot이라는 Python 모듈을 설치하십시오.

Pip install pyscreenshot

이제 다음과 같이 필요한 모듈을 가져옵니다.

import pyscreenshot as ImageGrab

다음 코드 줄을 사용하여 스크린 샷을 얻습니다.

image = ImageGrab.grab()

주어진 위치에 스크린 샷을 저장하려면 다음 코드 줄을 사용하십시오.

image.save('d:/image123.png')

이제 스크린 샷을 그래프로 표시하려면 다음 Python 코드를 사용할 수 있습니다.

import numpy as np
import matplotlib.pyplot as plt
import pyscreenshot as ImageGrab
imageg = ImageGrab.grab()
plt.imshow(image, cmap='gray', interpolation='bilinear')
plt.show()

이 장에서는 모바일 장치의 Python 디지털 포렌식과 관련된 개념에 대해 설명합니다.

소개

모바일 장치 포렌식은 조사 관심의 디지털 증거를 복구하기 위해 모바일 장치의 획득 및 분석을 다루는 디지털 포렌식의 한 분야입니다. 모바일 기기에는 위치와 관련된 유용한 정보를 제공하는 데 유용한 통신 시스템이 내장되어 있기 때문에이 지점은 컴퓨터 포렌식과 다릅니다.

디지털 포렌식에서 스마트 폰의 사용이 날마다 증가하고 있지만, 여전히 이질성으로 인해 비표준으로 간주됩니다. 반면에 하드 디스크와 같은 컴퓨터 하드웨어는 표준으로 간주되고 안정된 분야로 개발되었습니다. 디지털 포렌식 산업에서는 스마트 폰과 같은 일시적인 증거가있는 비표준 장치에 사용되는 기술에 대해 많은 논쟁이 있습니다.

모바일 장치에서 추출 가능한 아티팩트

최신 모바일 장치는 통화 기록이나 SMS 메시지 만있는 구형 전화기에 비해 많은 디지털 정보를 보유하고 있습니다. 따라서 모바일 장치는 조사자에게 사용자에 대한 많은 통찰력을 제공 할 수 있습니다. 모바일 장치에서 추출 할 수있는 일부 인공물은 다음과 같습니다.

  • Messages − 이것들은 소유자의 마음 상태를 드러내고 이전에 알려지지 않은 정보를 조사자에게 줄 수있는 유용한 인공물입니다.

  • Location History− 위치 기록 데이터는 조사관이 사람의 특정 위치를 확인하는 데 사용할 수있는 유용한 인공물입니다.

  • Applications Installed − 설치된 애플리케이션 종류에 액세스하여 조사자는 모바일 사용자의 습관과 생각에 대한 통찰력을 얻습니다.

Python의 증거 소스 및 처리

스마트 폰에는 SQLite 데이터베이스와 PLIST 파일이 주요 증거 자료로 사용됩니다. 이 섹션에서는 파이썬으로 증거 소스를 처리 할 것입니다.

PLIST 파일 분석

PLIST (속성 목록)는 특히 iPhone 장치에 응용 프로그램 데이터를 저장하기위한 유연하고 편리한 형식입니다. 확장을 사용합니다..plist. 번들 및 애플리케이션에 대한 정보를 저장하는 데 사용되는 이러한 종류의 파일입니다. 두 가지 형식이 있습니다.XMLbinary. 다음 Python 코드는 PLIST 파일을 열고 읽습니다. 계속 진행하기 전에Info.plist 파일.

먼저,라는 타사 라이브러리를 설치합니다. biplist 다음 명령으로-

Pip install biplist

이제 plist 파일을 처리하기 위해 유용한 라이브러리를 가져옵니다.

import biplist
import os
import sys

이제 main 메소드에서 다음 명령을 사용하여 plist 파일을 변수로 읽을 수 있습니다.

def main(plist):
   try:
      data = biplist.readPlist(plist)
   except (biplist.InvalidPlistException,biplist.NotBinaryPlistException) as e:
print("[-] Invalid PLIST file - unable to be opened by biplist")
sys.exit(1)

이제 콘솔에서 데이터를 읽거나이 변수에서 직접 인쇄 할 수 있습니다.

SQLite 데이터베이스

SQLite는 모바일 장치에서 기본 데이터 저장소 역할을합니다. SQLite는 자체 포함, 서버리스, 제로 구성, 트랜잭션 SQL 데이터베이스 엔진을 구현하는 프로세스 내 라이브러리입니다. 제로 구성되는 데이터베이스이므로 다른 데이터베이스와 달리 시스템에서 구성 할 필요가 없습니다.

당신이 SQLite는 데이터베이스와 초보자 또는 익숙를하는 경우, 당신은 링크 따를 수 www.tutorialspoint.com/sqlite/index.htm을 해당 링크를 따를 수 있습니다, 또한 www.tutorialspoint.com/sqlite/sqlite_python.htm 당신이 원하는 경우를 Python으로 SQLite에 대해 자세히 알아보십시오.

모바일 포렌식 중에 우리는 sms.db 모바일 장치의 파일을 저장하고 message표. Python에는sqlite3SQLite 데이터베이스와 연결하기 위해. 다음 명령으로 동일하게 가져올 수 있습니다-

import sqlite3

이제 다음 명령을 사용하여 데이터베이스에 연결할 수 있습니다. sms.db 모바일 기기의 경우 −

Conn = sqlite3.connect(‘sms.db’)
C = conn.cursor()

여기서 C는 데이터베이스와 상호 작용할 수있는 커서 객체입니다.

이제 특정 명령을 실행하고 싶다면 abc table, 다음 명령의 도움으로 수행 할 수 있습니다-

c.execute(“Select * from abc”)
c.close()

위 명령의 결과는 cursor목적. 마찬가지로 우리는fetchall() 우리가 조작 할 수있는 변수에 결과를 덤프하는 방법.

다음 명령을 사용하여 메시지 테이블의 열 이름 데이터를 가져올 수 있습니다. sms.db

c.execute(“pragma table_info(message)”)
table_data = c.fetchall()
columns = [x[1] for x in table_data

여기서 우리는 SQLite 환경 내에서 다양한 환경 변수와 상태 플래그를 제어하는 ​​데 사용되는 특수 명령 인 SQLite PRAGMA 명령을 사용하고 있습니다. 위의 명령에서fetchall()메서드는 결과의 튜플을 반환합니다. 각 열의 이름은 각 튜플의 첫 번째 인덱스에 저장됩니다.

이제 다음 명령을 사용하여 테이블에서 모든 데이터를 쿼리하고 이름이 지정된 변수에 저장할 수 있습니다. data_msg

c.execute(“Select * from message”)
data_msg = c.fetchall()

위의 명령은 변수에 데이터를 저장하고, 또한 다음을 사용하여 CSV 파일에 위의 데이터를 쓸 수도 있습니다. csv.writer() 방법.

iTunes 백업

iTunes에서 만든 백업에서 iPhone 모바일 포렌식을 수행 할 수 있습니다. 법의학 검사관은 iTunes를 통해 얻은 iPhone 논리적 백업 분석에 의존합니다. AFC (Apple 파일 연결) 프로토콜은 iTunes에서 백업을 수행하는 데 사용됩니다. 또한 백업 프로세스는 에스크로 키 레코드를 제외하고 iPhone에서 아무것도 수정하지 않습니다.

이제 디지털 포렌식 전문가가 iTunes 백업 기술을 이해하는 것이 왜 중요한지 의문이 생깁니다. 아이폰이 아닌 피의자의 컴퓨터에 직접 접속할 경우 중요하다. 아이폰과 컴퓨터를 동기화 할 때 아이폰에있는 대부분의 정보가 컴퓨터에 백업 될 가능성이 있기 때문이다.

백업 프로세스 및 위치

Apple 제품이 컴퓨터에 백업 될 때마다 iTunes와 동기화되며 장비의 고유 ID가있는 특정 폴더가 있습니다. 최신 백업 형식에서 파일은 파일 이름의 처음 두 16 진수 문자를 포함하는 하위 폴더에 저장됩니다. 이러한 백업 파일에는 Manifest.db라는 데이터베이스와 함께 유용한 info.plist와 같은 파일이 있습니다. 다음 표는 iTunes 백업의 운영 체제에 따라 다른 백업 위치를 보여줍니다.

OS 백업 위치
Win7 C : \ Users \ [사용자 이름] \ AppData \ Roaming \ AppleComputer \ MobileSync \ Backup \
맥 OS X ~ / Library / Application Suport / MobileSync / Backup /

Python으로 iTunes 백업을 처리하려면 먼저 운영 체제에 따라 백업 위치의 모든 백업을 식별해야합니다. 그런 다음 각 백업을 반복하고 Manifest.db 데이터베이스를 읽습니다.

이제 다음 Python 코드의 도움으로 동일한 작업을 수행 할 수 있습니다.

먼저 다음과 같이 필요한 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse
import logging
import os

from shutil import copyfile
import sqlite3
import sys
logger = logging.getLogger(__name__)

이제 iTunes 백업 및 원하는 출력 폴더를 나타내는 INPUT_DIR 및 OUTPUT_DIR의 두 위치 인수를 제공하십시오.

if __name__ == "__main__":
   parser.add_argument("INPUT_DIR",help = "Location of folder containing iOS backups, ""e.g. ~\Library\Application Support\MobileSync\Backup folder")
   parser.add_argument("OUTPUT_DIR", help = "Output Directory")
   parser.add_argument("-l", help = "Log file path",default = __file__[:-2] + "log")
   parser.add_argument("-v", help = "Increase verbosity",action = "store_true") args = parser.parse_args()

이제 다음과 같이 로그를 설정하십시오.

if args.v:
   logger.setLevel(logging.DEBUG)
else:
   logger.setLevel(logging.INFO)

이제이 로그의 메시지 형식을 다음과 같이 설정하십시오.

msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-13s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt = msg_fmt)

fhndl = logging.FileHandler(args.l, mode = 'a')
fhndl.setFormatter(fmt = msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting iBackup Visualizer")
logger.debug("Supplied arguments: {}".format(" ".join(sys.argv[1:])))
logger.debug("System: " + sys.platform)
logger.debug("Python Version: " + sys.version)

다음 코드 줄은 다음을 사용하여 원하는 출력 디렉터리에 필요한 폴더를 만듭니다. os.makedirs() 기능-

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)

이제 다음과 같이 제공된 입력 및 출력 디렉토리를 main () 함수에 전달합니다.

if os.path.exists(args.INPUT_DIR) and os.path.isdir(args.INPUT_DIR):
   main(args.INPUT_DIR, args.OUTPUT_DIR)
else:
   logger.error("Supplied input directory does not exist or is not ""a directory")
   sys.exit(1)

이제 쓰기 main() 추가로 호출 할 함수 backup_summary() 입력 폴더에있는 모든 백업을 식별하는 기능-

def main(in_dir, out_dir):
   backups = backup_summary(in_dir)
def backup_summary(in_dir):
   logger.info("Identifying all iOS backups in {}".format(in_dir))
   root = os.listdir(in_dir)
   backups = {}
   
   for x in root:
      temp_dir = os.path.join(in_dir, x)
      if os.path.isdir(temp_dir) and len(x) == 40:
         num_files = 0
         size = 0
         
         for root, subdir, files in os.walk(temp_dir):
            num_files += len(files)
            size += sum(os.path.getsize(os.path.join(root, name))
               for name in files)
         backups[x] = [temp_dir, num_files, size]
   return backups

이제 다음과 같이 각 백업의 요약을 콘솔에 인쇄하십시오.

print("Backup Summary")
print("=" * 20)

if len(backups) > 0:
   for i, b in enumerate(backups):
      print("Backup No.: {} \n""Backup Dev. Name: {} \n""# Files: {} \n""Backup Size (Bytes): {}\n".format(i, b, backups[b][1], backups[b][2]))

이제 Manifest.db 파일의 내용을 db_items라는 변수에 덤프합니다.

try:
   db_items = process_manifest(backups[b][0])
   except IOError:
      logger.warn("Non-iOS 10 backup encountered or " "invalid backup. Continuing to next backup.")
continue

이제 백업의 디렉토리 경로를 사용할 함수를 정의하겠습니다.

def process_manifest(backup):
   manifest = os.path.join(backup, "Manifest.db")
   
   if not os.path.exists(manifest):
      logger.error("Manifest DB not found in {}".format(manifest))
      raise IOError

이제 SQLite3를 사용하여 c라는 커서로 데이터베이스에 연결합니다.

c = conn.cursor()
items = {}

for row in c.execute("SELECT * from Files;"):
   items[row[0]] = [row[2], row[1], row[3]]
return items

create_files(in_dir, out_dir, b, db_items)
   print("=" * 20)
else:
   logger.warning("No valid backups found. The input directory should be
      " "the parent-directory immediately above the SHA-1 hash " "iOS device backups")
      sys.exit(2)

이제 create_files() 다음과 같이 방법-

def create_files(in_dir, out_dir, b, db_items):
   msg = "Copying Files for backup {} to {}".format(b, os.path.join(out_dir, b))
   logger.info(msg)

이제 각 키를 반복합니다. db_items 사전 −

for x, key in enumerate(db_items):
   if db_items[key][0] is None or db_items[key][0] == "":
      continue
   else:
      dirpath = os.path.join(out_dir, b,
os.path.dirname(db_items[key][0]))
   filepath = os.path.join(out_dir, b, db_items[key][0])
   
   if not os.path.exists(dirpath):
      os.makedirs(dirpath)
      original_dir = b + "/" + key[0:2] + "/" + key
   path = os.path.join(in_dir, original_dir)
   
   if os.path.exists(filepath):
      filepath = filepath + "_{}".format(x)

이제 shutil.copyfile() 백업 파일을 복사하는 방법은 다음과 같습니다.

try:
   copyfile(path, filepath)
   except IOError:
      logger.debug("File not found in backup: {}".format(path))
         files_not_found += 1
   if files_not_found > 0:
      logger.warning("{} files listed in the Manifest.db not" "found in
backup".format(files_not_found))
   copyfile(os.path.join(in_dir, b, "Info.plist"), os.path.join(out_dir, b,
"Info.plist"))
   copyfile(os.path.join(in_dir, b, "Manifest.db"), os.path.join(out_dir, b,
"Manifest.db"))
   copyfile(os.path.join(in_dir, b, "Manifest.plist"), os.path.join(out_dir, b,
"Manifest.plist"))
   copyfile(os.path.join(in_dir, b, "Status.plist"),os.path.join(out_dir, b,
"Status.plist"))

위의 Python 스크립트를 사용하여 출력 폴더에서 업데이트 된 백업 파일 구조를 가져올 수 있습니다. 우리는 사용할 수 있습니다pycrypto python 라이브러리를 사용하여 백업을 해독합니다.

와이파이

모바일 장치는 어디에서나 사용할 수있는 Wi-Fi 네트워크를 통해 연결하여 외부 세계에 연결할 수 있습니다. 때로는 장치가 이러한 개방형 네트워크에 자동으로 연결됩니다.

iPhone의 경우 기기가 연결된 열린 Wi-Fi 연결 목록이 PLIST 파일에 저장됩니다. com.apple.wifi.plist. 이 파일에는 Wi-Fi SSID, BSSID 및 연결 시간이 포함됩니다.

Python을 사용하여 표준 Cellebrite XML 보고서에서 Wi-Fi 세부 정보를 추출해야합니다. 이를 위해서는 Wi-Fi 네트워크 이름을 사용하여 기기의 위치를 ​​찾는 데 사용할 수있는 인기있는 플랫폼 인 WIGLE (Wireless Geographic Logging Engine)의 API를 사용해야합니다.

파이썬 라이브러리를 사용할 수 있습니다. requestsWIGLE에서 API에 액세스합니다. 다음과 같이 설치할 수 있습니다.

pip install requests

WIGLE의 API

WIGLE 웹 사이트에 등록해야합니다. https://wigle.net/accountWIGLE에서 무료 API를 얻으려면. WIGEL의 API를 통해 사용자 장치 및 연결에 대한 정보를 가져 오는 Python 스크립트는 아래에서 설명합니다.

먼저, 다른 작업을 처리하기 위해 다음 라이브러리를 가져옵니다.

from __future__ import print_function

import argparse
import csv
import os
import sys
import xml.etree.ElementTree as ET
import requests

이제 두 개의 위치 인수를 제공하십시오. INPUT_FILEOUTPUT_CSV Wi-Fi MAC 주소가있는 입력 파일과 원하는 출력 CSV 파일을 각각 나타냅니다.

if __name__ == "__main__":
   parser.add_argument("INPUT_FILE", help = "INPUT FILE with MAC Addresses")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV File")
   parser.add_argument("-t", help = "Input type: Cellebrite XML report or TXT
file",choices = ('xml', 'txt'), default = "xml")
   parser.add_argument('--api', help = "Path to API key
   file",default = os.path.expanduser("~/.wigle_api"),
   type = argparse.FileType('r'))
   args = parser.parse_args()

이제 다음 코드 줄은 입력 파일이 존재하고 파일인지 확인합니다. 그렇지 않은 경우 스크립트를 종료합니다.

if not os.path.exists(args.INPUT_FILE) or \ not os.path.isfile(args.INPUT_FILE):
   print("[-] {} does not exist or is not a
file".format(args.INPUT_FILE))
   sys.exit(1)
directory = os.path.dirname(args.OUTPUT_CSV)
if directory != '' and not os.path.exists(directory):
   os.makedirs(directory)
api_key = args.api.readline().strip().split(":")

이제 다음과 같이 main에 인수를 전달하십시오.

main(args.INPUT_FILE, args.OUTPUT_CSV, args.t, api_key)
def main(in_file, out_csv, type, api_key):
   if type == 'xml':
      wifi = parse_xml(in_file)
   else:
      wifi = parse_txt(in_file)
query_wigle(wifi, out_csv, api_key)

이제 다음과 같이 XML 파일을 구문 분석합니다.

def parse_xml(xml_file):
   wifi = {}
   xmlns = "{http://pa.cellebrite.com/report/2.0}"
   print("[+] Opening {} report".format(xml_file))
   
   xml_tree = ET.parse(xml_file)
   print("[+] Parsing report for all connected WiFi addresses")
   
   root = xml_tree.getroot()

이제 다음과 같이 루트의 자식 요소를 반복합니다.

for child in root.iter():
   if child.tag == xmlns + "model":
      if child.get("type") == "Location":
         for field in child.findall(xmlns + "field"):
            if field.get("name") == "TimeStamp":
               ts_value = field.find(xmlns + "value")
               try:
               ts = ts_value.text
               except AttributeError:
continue

이제 값의 텍스트에 'ssid'문자열이 있는지 확인합니다.

if "SSID" in value.text:
   bssid, ssid = value.text.split("\t")
   bssid = bssid[7:]
   ssid = ssid[6:]

이제 다음과 같이 BSSID, SSID 및 타임 스탬프를 wifi 사전에 추가해야합니다.

if bssid in wifi.keys():

wifi[bssid]["Timestamps"].append(ts)
   wifi[bssid]["SSID"].append(ssid)
else:
   wifi[bssid] = {"Timestamps": [ts], "SSID":
[ssid],"Wigle": {}}
return wifi

XML 파서보다 훨씬 간단한 텍스트 파서가 아래에 나와 있습니다.

def parse_txt(txt_file):
   wifi = {}
   print("[+] Extracting MAC addresses from {}".format(txt_file))
   
   with open(txt_file) as mac_file:
      for line in mac_file:
         wifi[line.strip()] = {"Timestamps": ["N/A"], "SSID":
["N/A"],"Wigle": {}}
return wifi

이제 요청 모듈을 사용하여 WIGLE API전화 및 이동해야 query_wigle() 방법-

def query_wigle(wifi_dictionary, out_csv, api_key):
   print("[+] Querying Wigle.net through Python API for {} "
"APs".format(len(wifi_dictionary)))
   for mac in wifi_dictionary:

   wigle_results = query_mac_addr(mac, api_key)
def query_mac_addr(mac_addr, api_key):

   query_url = "https://api.wigle.net/api/v2/network/search?" \
"onlymine = false&freenet = false&paynet = false" \ "&netid = {}".format(mac_addr)
   req = requests.get(query_url, auth = (api_key[0], api_key[1]))
   return req.json()

실제로 WIGLE API 호출에 대한 일일 제한이 있으며, 그 제한을 초과하면 다음과 같은 오류를 표시해야합니다.

try:
   if wigle_results["resultCount"] == 0:
      wifi_dictionary[mac]["Wigle"]["results"] = []
         continue
   else:
      wifi_dictionary[mac]["Wigle"] = wigle_results
except KeyError:
   if wigle_results["error"] == "too many queries today":
      print("[-] Wigle daily query limit exceeded")
      wifi_dictionary[mac]["Wigle"]["results"] = []
      continue
   else:
      print("[-] Other error encountered for " "address {}: {}".format(mac,
wigle_results['error']))
   wifi_dictionary[mac]["Wigle"]["results"] = []
   continue
prep_output(out_csv, wifi_dictionary)

이제 우리는 prep_output() 사전을 쓰기 쉬운 덩어리로 편 평화하는 방법-

def prep_output(output, data):
   csv_data = {}
   google_map = https://www.google.com/maps/search/

이제 다음과 같이 지금까지 수집 한 모든 데이터에 액세스하십시오.

for x, mac in enumerate(data):
   for y, ts in enumerate(data[mac]["Timestamps"]):
      for z, result in enumerate(data[mac]["Wigle"]["results"]):
         shortres = data[mac]["Wigle"]["results"][z]
         g_map_url = "{}{},{}".format(google_map, shortres["trilat"],shortres["trilong"])

이제이 장의 이전 스크립트에서 수행 한 것처럼 CSV 파일로 출력을 작성할 수 있습니다. write_csv() 함수.

이 장에서는 Python 디지털 포렌식을 사용하여 임베디드 메타 데이터를 조사하는 방법에 대해 자세히 알아 봅니다.

소개

포함 된 메타 데이터는 해당 데이터로 설명 된 개체가있는 동일한 파일에 저장된 데이터에 대한 정보입니다. 즉, 디지털 파일 자체에 저장된 디지털 자산에 대한 정보입니다. 항상 파일과 연관되며 분리 될 수 없습니다.

디지털 포렌식의 경우 특정 파일에 대한 모든 정보를 추출 할 수 없습니다. 반면에 포함 된 메타 데이터는 조사에 중요한 정보를 제공 할 수 있습니다. 예를 들어, 텍스트 파일의 메타 데이터에는 작성자, 길이, 작성된 날짜 및 해당 문서에 대한 간단한 요약 정보가 포함될 수 있습니다. 디지털 이미지에는 이미지 길이, 셔터 속도 등과 같은 메타 데이터가 포함될 수 있습니다.

메타 데이터 속성 및 그 추출을 포함하는 아티팩트

이 섹션에서는 메타 데이터 속성을 포함하는 다양한 아티팩트와 Python을 사용한 추출 프로세스에 대해 알아 봅니다.

오디오 및 비디오

이들은 메타 데이터가 포함 된 매우 일반적인 두 가지 아티팩트입니다. 이 메타 데이터는 조사 목적으로 추출 할 수 있습니다.

다음 Python 스크립트를 사용하여 오디오 또는 MP3 파일과 비디오 또는 MP4 파일에서 공통 속성 또는 메타 데이터를 추출 할 수 있습니다.

이 스크립트의 경우 오디오 및 비디오 파일에서 메타 데이터를 추출 할 수있는 mutagen이라는 타사 Python 라이브러리를 설치해야합니다. 다음 명령을 사용하여 설치할 수 있습니다.

pip install mutagen

이 Python 스크립트를 위해 가져와야하는 유용한 라이브러리 중 일부는 다음과 같습니다.

from __future__ import print_function

import argparse
import json
import mutagen

명령 줄 처리기는 MP3 또는 MP4 파일의 경로를 나타내는 하나의 인수를 사용합니다. 그런 다음mutagen.file() 다음과 같이 파일에 대한 핸들을 여는 방법-

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Python Metadata Extractor')
   parser.add_argument("AV_FILE", help="File to extract metadata from")
   args = parser.parse_args()
   av_file = mutagen.File(args.AV_FILE)
   file_ext = args.AV_FILE.rsplit('.', 1)[-1]
   
   if file_ext.lower() == 'mp3':
      handle_id3(av_file)
   elif file_ext.lower() == 'mp4':
      handle_mp4(av_file)

이제 두 개의 핸들을 사용해야합니다. 하나는 MP3에서 데이터를 추출하고 다른 하나는 MP4 파일에서 데이터를 추출하는 데 사용됩니다. 이러한 핸들을 다음과 같이 정의 할 수 있습니다.

def handle_id3(id3_file):
   id3_frames = {'TIT2': 'Title', 'TPE1': 'Artist', 'TALB': 'Album','TXXX':
      'Custom', 'TCON': 'Content Type', 'TDRL': 'Date released','COMM': 'Comments',
         'TDRC': 'Recording Date'}
   print("{:15} | {:15} | {:38} | {}".format("Frame", "Description","Text","Value"))
   print("-" * 85)
   
   for frames in id3_file.tags.values():
      frame_name = id3_frames.get(frames.FrameID, frames.FrameID)
      desc = getattr(frames, 'desc', "N/A")
      text = getattr(frames, 'text', ["N/A"])[0]
      value = getattr(frames, 'value', "N/A")
      
      if "date" in frame_name.lower():
         text = str(text)
      print("{:15} | {:15} | {:38} | {}".format(
         frame_name, desc, text, value))
def handle_mp4(mp4_file):
   cp_sym = u"\u00A9"
   qt_tag = {
      cp_sym + 'nam': 'Title', cp_sym + 'art': 'Artist',
      cp_sym + 'alb': 'Album', cp_sym + 'gen': 'Genre',
      'cpil': 'Compilation', cp_sym + 'day': 'Creation Date',
      'cnID': 'Apple Store Content ID', 'atID': 'Album Title ID',
      'plID': 'Playlist ID', 'geID': 'Genre ID', 'pcst': 'Podcast',
      'purl': 'Podcast URL', 'egid': 'Episode Global ID',
      'cmID': 'Camera ID', 'sfID': 'Apple Store Country',
      'desc': 'Description', 'ldes': 'Long Description'}
genre_ids = json.load(open('apple_genres.json'))

이제이 MP4 파일을 다음과 같이 반복해야합니다.

print("{:22} | {}".format('Name', 'Value'))
print("-" * 40)

for name, value in mp4_file.tags.items():
   tag_name = qt_tag.get(name, name)
   
   if isinstance(value, list):
      value = "; ".join([str(x) for x in value])
   if name == 'geID':
      value = "{}: {}".format(
      value, genre_ids[str(value)].replace("|", " - "))
   print("{:22} | {}".format(tag_name, value))

위의 스크립트는 MP3 및 MP4 파일에 대한 추가 정보를 제공합니다.

이미지

이미지에는 파일 형식에 따라 다른 종류의 메타 데이터가 포함될 수 있습니다. 그러나 대부분의 이미지에는 GPS 정보가 포함되어 있습니다. 타사 Python 라이브러리를 사용하여이 GPS 정보를 추출 할 수 있습니다. 다음 Python 스크립트를 사용하여 동일한 작업을 수행 할 수 있습니다.

먼저 타사 Python 라이브러리를 다운로드하십시오. Python Imaging Library (PIL) 다음과 같이-

pip install pillow

이렇게하면 이미지에서 메타 데이터를 추출하는 데 도움이됩니다.

이미지에 포함 된 GPS 세부 정보를 KML 파일에 쓸 수도 있지만이를 위해서는 이름이 지정된 타사 Python 라이브러리를 다운로드해야합니다. simplekml 다음과 같이-

pip install simplekml

이 스크립트에서 먼저 다음 라이브러리를 가져와야합니다.

from __future__ import print_function
import argparse

from PIL import Image
from PIL.ExifTags import TAGS

import simplekml
import sys

이제 명령 줄 처리기는 기본적으로 사진의 파일 경로를 나타내는 하나의 위치 인수를 허용합니다.

parser = argparse.ArgumentParser('Metadata from images')
parser.add_argument('PICTURE_FILE', help = "Path to picture")
args = parser.parse_args()

이제 좌표 정보를 채울 URL을 지정해야합니다. URL은gmapsopen_maps. 또한 PIL 라이브러리에서 제공하는 DMS (Degree Minute Second) 튜플 좌표를 십진수로 변환하는 함수가 필요합니다. 다음과 같이 할 수 있습니다-

gmaps = "https://www.google.com/maps?q={},{}"
open_maps = "http://www.openstreetmap.org/?mlat={}&mlon={}"

def process_coords(coord):
   coord_deg = 0
   
   for count, values in enumerate(coord):
      coord_deg += (float(values[0]) / values[1]) / 60**count
   return coord_deg

이제 우리는 image.open() 파일을 PIL 객체로 여는 기능.

img_file = Image.open(args.PICTURE_FILE)
exif_data = img_file._getexif()

if exif_data is None:
   print("No EXIF data found")
   sys.exit()
for name, value in exif_data.items():
   gps_tag = TAGS.get(name, name)
   if gps_tag is not 'GPSInfo':
      continue

찾은 후 GPSInfo 태그, 우리는 GPS 참조를 저장하고 좌표를 process_coords() 방법.

lat_ref = value[1] == u'N'
lat = process_coords(value[2])

if not lat_ref:
   lat = lat * -1
lon_ref = value[3] == u'E'
lon = process_coords(value[4])

if not lon_ref:
   lon = lon * -1

자, 시작 kml 의 개체 simplekml 다음과 같이 라이브러리-

kml = simplekml.Kml()
kml.newpoint(name = args.PICTURE_FILE, coords = [(lon, lat)])
kml.save(args.PICTURE_FILE + ".kml")

이제 다음과 같이 처리 된 정보에서 좌표를 인쇄 할 수 있습니다.

print("GPS Coordinates: {}, {}".format(lat, lon))
print("Google Maps URL: {}".format(gmaps.format(lat, lon)))
print("OpenStreetMap URL: {}".format(open_maps.format(lat, lon)))
print("KML File {} created".format(args.PICTURE_FILE + ".kml"))

PDF 문서

PDF 문서에는 이미지, 텍스트, 양식 등 다양한 미디어가 있습니다. PDF 문서에 포함 된 메타 데이터를 추출 할 때 결과 데이터를 XMP (Extensible Metadata Platform) 형식으로 얻을 수 있습니다. 다음 Python 코드를 사용하여 메타 데이터를 추출 할 수 있습니다.

먼저,라는 타사 Python 라이브러리를 설치합니다. PyPDF2XMP 형식으로 저장된 메타 데이터를 읽습니다. 다음과 같이 설치할 수 있습니다.

pip install PyPDF2

이제 PDF 파일에서 메타 데이터를 추출하기 위해 다음 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser, FileType

import datetime
from PyPDF2 import PdfFileReader
import sys

이제 명령 줄 처리기는 기본적으로 PDF 파일의 파일 경로를 나타내는 하나의 위치 인수를 허용합니다.

parser = argparse.ArgumentParser('Metadata from PDF')
parser.add_argument('PDF_FILE', help='Path to PDF file',type=FileType('rb'))
args = parser.parse_args()

이제 우리는 getXmpMetadata() 다음과 같이 사용 가능한 메타 데이터를 포함하는 객체를 제공하는 방법-

pdf_file = PdfFileReader(args.PDF_FILE)
xmpm = pdf_file.getXmpMetadata()

if xmpm is None:
   print("No XMP metadata found in document.")
   sys.exit()

우리는 사용할 수 있습니다 custom_print() 제목, 작성자, 기여자 등 관련 값을 추출하여 출력하는 방법은 다음과 같습니다.

custom_print("Title: {}", xmpm.dc_title)
custom_print("Creator(s): {}", xmpm.dc_creator)
custom_print("Contributors: {}", xmpm.dc_contributor)
custom_print("Subject: {}", xmpm.dc_subject)
custom_print("Description: {}", xmpm.dc_description)
custom_print("Created: {}", xmpm.xmp_createDate)
custom_print("Modified: {}", xmpm.xmp_modifyDate)
custom_print("Event Dates: {}", xmpm.dc_date)

우리는 또한 정의 할 수 있습니다 custom_print() 다음과 같이 여러 소프트웨어를 사용하여 PDF를 생성하는 경우 방법-

def custom_print(fmt_str, value):
   if isinstance(value, list):
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, dict):
      fmt_value = [":".join((k, v)) for k, v in value.items()]
      print(fmt_str.format(", ".join(value)))
   elif isinstance(value, str) or isinstance(value, bool):
      print(fmt_str.format(value))
   elif isinstance(value, bytes):
      print(fmt_str.format(value.decode()))
   elif isinstance(value, datetime.datetime):
      print(fmt_str.format(value.isoformat()))
   elif value is None:
      print(fmt_str.format("N/A"))
   else:
      print("warn: unhandled type {} found".format(type(value)))

다음과 같이 소프트웨어에 의해 저장된 다른 사용자 정의 속성을 추출 할 수도 있습니다.

if xmpm.custom_properties:
   print("Custom Properties:")
   
   for k, v in xmpm.custom_properties.items():
      print("\t{}: {}".format(k, v))

위의 스크립트는 PDF 문서를 읽고 해당 PDF를 만든 소프트웨어에 저장된 일부 사용자 정의 속성을 포함하여 XMP 형식으로 저장된 메타 데이터를 인쇄합니다.

Windows 실행 파일

때때로 의심 스럽거나 승인되지 않은 실행 파일이 발생할 수 있습니다. 그러나 조사 목적으로는 메타 데이터가 포함되어 있기 때문에 유용 할 수 있습니다. 위치, 용도 및 제조업체, 컴파일 날짜 등과 같은 기타 속성과 같은 정보를 얻을 수 있습니다. 다음 Python 스크립트의 도움으로 컴파일 날짜, 헤더에서 유용한 데이터, 가져 오기 및 내보내기 기호를 가져올 수 있습니다.

이를 위해 먼저 타사 Python 라이브러리를 설치하십시오. pefile. 다음과 같이 할 수 있습니다-

pip install pefile

성공적으로 설치했으면 다음과 같이 다음 라이브러리를 가져옵니다.

from __future__ import print_function

import argparse
from datetime import datetime
from pefile import PE

이제 명령 줄 처리기는 기본적으로 실행 파일의 파일 경로를 나타내는 하나의 위치 인수를 허용합니다. 상세하고 장황한 방식으로 또는 단순화 된 방식으로 필요한 경우 출력 스타일을 선택할 수도 있습니다. 이를 위해 아래와 같이 선택적 인수를 제공해야합니다.

parser = argparse.ArgumentParser('Metadata from executable file')
parser.add_argument("EXE_FILE", help = "Path to exe file")
parser.add_argument("-v", "--verbose", help = "Increase verbosity of output",
action = 'store_true', default = False)
args = parser.parse_args()

이제 PE 클래스를 사용하여 입력 실행 파일을로드합니다. 또한 다음을 사용하여 실행 가능한 데이터를 사전 개체에 덤프합니다.dump_dict() 방법.

pe = PE(args.EXE_FILE)
ped = pe.dump_dict()

아래 표시된 코드를 사용하여 포함 된 저작자, 버전 및 컴파일 시간과 같은 기본 파일 메타 데이터를 추출 할 수 있습니다.

file_info = {}
for structure in pe.FileInfo:
   if structure.Key == b'StringFileInfo':
      for s_table in structure.StringTable:
         for key, value in s_table.entries.items():
            if value is None or len(value) == 0:
               value = "Unknown"
            file_info[key] = value
print("File Information: ")
print("==================")

for k, v in file_info.items():
   if isinstance(k, bytes):
      k = k.decode()
   if isinstance(v, bytes):
      v = v.decode()
   print("{}: {}".format(k, v))
comp_time = ped['FILE_HEADER']['TimeDateStamp']['Value']
comp_time = comp_time.split("[")[-1].strip("]")
time_stamp, timezone = comp_time.rsplit(" ", 1)
comp_time = datetime.strptime(time_stamp, "%a %b %d %H:%M:%S %Y")
print("Compiled on {} {}".format(comp_time, timezone.strip()))

다음과 같이 헤더에서 유용한 데이터를 추출 할 수 있습니다.

for section in ped['PE Sections']:
   print("Section '{}' at {}: {}/{} {}".format(
      section['Name']['Value'], hex(section['VirtualAddress']['Value']),
      section['Misc_VirtualSize']['Value'],
      section['SizeOfRawData']['Value'], section['MD5'])
   )

이제 아래와 같이 실행 파일에서 가져 오기 및 내보내기 목록을 추출하십시오.

if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
   print("\nImports: ")
   print("=========")
   
   for dir_entry in pe.DIRECTORY_ENTRY_IMPORT:
      dll = dir_entry.dll
      
      if not args.verbose:
         print(dll.decode(), end=", ")
         continue
      name_list = []
      
      for impts in dir_entry.imports:
         if getattr(impts, "name", b"Unknown") is None:
            name = b"Unknown"
         else:
            name = getattr(impts, "name", b"Unknown")
			name_list.append([name.decode(), hex(impts.address)])
      name_fmt = ["{} ({})".format(x[0], x[1]) for x in name_list]
      print('- {}: {}'.format(dll.decode(), ", ".join(name_fmt)))
   if not args.verbose:
      print()

이제 인쇄 exports, namesaddresses 아래와 같이 코드를 사용하여-

if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
   print("\nExports: ")
   print("=========")
   
   for sym in pe.DIRECTORY_ENTRY_EXPORT.symbols:
      print('- {}: {}'.format(sym.name.decode(), hex(sym.address)))

위의 스크립트는 Windows 실행 파일의 헤더에서 기본 메타 데이터, 정보를 추출합니다.

Office 문서 메타 데이터

컴퓨터 작업의 대부분은 MS Office의 세 가지 응용 프로그램 인 Word, PowerPoint 및 Excel에서 수행됩니다. 이러한 파일에는 저작자 및 역사에 대한 흥미로운 정보를 노출 할 수있는 거대한 메타 데이터가 있습니다.

2007 년 단어 형식 (.docx), excel (.xlsx) 및 powerpoint (.pptx)의 메타 데이터는 XML 파일에 저장됩니다. 아래에 표시된 Python 스크립트를 사용하여 이러한 XML 파일을 Python에서 처리 할 수 ​​있습니다.

먼저 아래와 같이 필요한 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser
from datetime import datetime as dt
from xml.etree import ElementTree as etree

import zipfile
parser = argparse.ArgumentParser('Office Document Metadata’)
parser.add_argument("Office_File", help="Path to office file to read")
args = parser.parse_args()

이제 파일이 ZIP 파일인지 확인하십시오. 그렇지 않으면 오류를 발생시킵니다. 이제 파일을 열고 다음 코드를 사용하여 처리 할 핵심 요소를 추출합니다.

zipfile.is_zipfile(args.Office_File)
zfile = zipfile.ZipFile(args.Office_File)
core_xml = etree.fromstring(zfile.read('docProps/core.xml'))
app_xml = etree.fromstring(zfile.read('docProps/app.xml'))

이제 메타 데이터 추출을 시작하기위한 사전을 만듭니다.

core_mapping = {
   'title': 'Title',
   'subject': 'Subject',
   'creator': 'Author(s)',
   'keywords': 'Keywords',
   'description': 'Description',
   'lastModifiedBy': 'Last Modified By',
   'modified': 'Modified Date',
   'created': 'Created Date',
   'category': 'Category',
   'contentStatus': 'Status',
   'revision': 'Revision'
}

사용하다 iterchildren() XML 파일 내의 각 태그에 액세스하는 방법-

for element in core_xml.getchildren():
   for key, title in core_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

마찬가지로 문서의 내용에 대한 통계 정보가 포함 된 app.xml 파일에 대해이 작업을 수행합니다.

app_mapping = {
   'TotalTime': 'Edit Time (minutes)',
   'Pages': 'Page Count',
   'Words': 'Word Count',
   'Characters': 'Character Count',
   'Lines': 'Line Count',
   'Paragraphs': 'Paragraph Count',
   'Company': 'Company',
   'HyperlinkBase': 'Hyperlink Base',
   'Slides': 'Slide count',
   'Notes': 'Note Count',
   'HiddenSlides': 'Hidden Slide Count',
}
for element in app_xml.getchildren():
   for key, title in app_mapping.items():
      if key in element.tag:
         if 'date' in title.lower():
            text = dt.strptime(element.text, "%Y-%m-%dT%H:%M:%SZ")
         else:
            text = element.text
         print("{}: {}".format(title, text))

이제 위 스크립트를 실행 한 후 특정 문서에 대한 다른 세부 정보를 얻을 수 있습니다. 이 스크립트는 Office 2007 이상 버전 문서에만 적용 할 수 있습니다.

이 장에서는 Python을 사용하여 네트워크 포렌식을 수행하는 데 관련된 기본 사항을 설명합니다.

네트워크 포렌식 이해

네트워크 포렌식은 정보 수집, 증거 수집 또는 침입 감지를 위해 로컬 및 WAN (광역 네트워크)의 컴퓨터 네트워크 트래픽을 모니터링하고 분석하는 디지털 포렌식의 한 분야입니다. 네트워크 포렌식은 지적 재산 도난이나 정보 유출과 같은 디지털 범죄를 수사하는 데 중요한 역할을합니다. 네트워크 통신 그림은 조사자가 다음과 같은 몇 가지 중요한 질문을 해결하는 데 도움이됩니다.

  • 어떤 웹 사이트에 액세스 했습니까?

  • 네트워크에 어떤 종류의 콘텐츠가 업로드 되었습니까?

  • 네트워크에서 어떤 종류의 콘텐츠를 다운로드 했습니까?

  • 어떤 서버에 액세스하고 있습니까?

  • 누군가 회사 방화벽 외부로 민감한 정보를 보내고 있습니까?

Internet Evidence Finder (IEF)

IEF는 컴퓨터, 스마트 폰, 태블릿 등과 같은 다양한 디지털 미디어에서 발견 된 디지털 증거를 찾고, 분석하고, 제시하는 디지털 포렌식 도구입니다. 수천 명의 포렌식 전문가가 사용하는 매우 인기있는 도구입니다.

IEF 사용

인기로 인해 IEF는 법의학 전문가들이 많이 사용합니다. IEF의 일부 용도는 다음과 같습니다.

  • 강력한 검색 기능으로 인해 여러 파일 또는 데이터 미디어를 동시에 검색하는 데 사용됩니다.

  • 또한 새로운 조각 기술을 통해 RAM의 할당되지 않은 공간에서 삭제 된 데이터를 복구하는 데 사용됩니다.

  • 조사관이 웹 페이지를 연 날짜에 원래 형식으로 재 구축하려는 경우 IEF를 사용할 수 있습니다.

  • 논리 또는 물리 디스크 볼륨을 검색하는데도 사용됩니다.

Python을 사용하여 IEF에서 CSV로 보고서 덤프

IEF는 SQLite 데이터베이스에 데이터를 저장하고 다음 Python 스크립트는 IEF 데이터베이스 내에서 결과 테이블을 동적으로 식별하고 각 CSV 파일에 덤프합니다.

이 프로세스는 아래 표시된 단계에서 수행됩니다.

  • 먼저 .db 확장자로 끝나는 SQLite 데이터베이스 파일이 될 IEF 결과 데이터베이스를 생성합니다.

  • 그런 다음 해당 데이터베이스를 쿼리하여 모든 테이블을 식별하십시오.

  • 마지막으로이 결과 테이블을 개별 CSV 파일에 씁니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

Python 스크립트의 경우 다음과 같이 필요한 라이브러리를 가져옵니다.

from __future__ import print_function

import argparse
import csv
import os
import sqlite3
import sys

이제 IEF 데이터베이스 파일의 경로를 제공해야합니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

이제 다음과 같이 IEF 데이터베이스의 존재를 확인합니다.

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_DIR)
else:
   print("[-] Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
   sys.exit(1)

이제 이전 스크립트에서했던 것처럼 다음과 같이 SQLite 데이터베이스와 연결하여 커서를 통해 쿼리를 실행합니다.

def main(database, out_directory):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

다음 코드 줄은 데이터베이스에서 테이블 이름을 가져옵니다-

print("List of all tables to extract")
c.execute("select * from sqlite_master where type = 'table'")
tables = [x[2] for x in c.fetchall() if not x[2].startswith('_') and not x[2].endswith('_DATA')]

이제 테이블에서 모든 데이터를 선택하고 fetchall() 커서 객체에 대한 메서드는 테이블의 데이터 전체를 포함하는 튜플 목록을 변수에 저장합니다.

print("Dumping {} tables to CSV files in {}".format(len(tables), out_directory))

for table in tables:
c.execute("pragma table_info('{}')".format(table))
table_columns = [x[1] for x in c.fetchall()]

c.execute("select * from '{}'".format(table))
table_data = c.fetchall()

이제 CSV_Writer() 방법은 CSV 파일에 내용을 작성합니다-

csv_name = table + '.csv'
csv_path = os.path.join(out_directory, csv_name)
print('[+] Writing {} table to {} CSV file'.format(table,csv_name))

with open(csv_path, "w", newline = "") as csvfile:
   csv_writer = csv.writer(csvfile)
   csv_writer.writerow(table_columns)
   csv_writer.writerows(table_data)

위의 스크립트는 IEF 데이터베이스의 테이블에서 모든 데이터를 가져 와서 선택한 CSV 파일에 내용을 씁니다.

캐시 된 데이터 작업

IEF 결과 데이터베이스에서 IEF 자체에서 지원할 필요가없는 더 많은 정보를 가져올 수 있습니다. 우리는 IEF 결과 데이터베이스를 사용하여 Yahoo, Google 등과 같은 이메일 서비스 제공 업체에서 정보 용 이중 제품인 캐시 된 데이터를 가져올 수 있습니다.

다음은 IEF 데이터베이스를 사용하여 Google Chrome에서 액세스 한 Yahoo 메일에서 캐시 된 데이터 정보에 액세스하기위한 Python 스크립트입니다. 단계는 마지막 Python 스크립트에서 수행 한 것과 다소 동일합니다.

먼저 다음과 같이 Python에 필요한 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse
import csv
import os
import sqlite3
import sys
import json

이제 마지막 스크립트에서 수행 한 것처럼 명령 줄 처리기가 허용하는 두 개의 위치 인수와 함께 IEF 데이터베이스 파일의 경로를 제공합니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('IEF to CSV')
   parser.add_argument("IEF_DATABASE", help="Input IEF database")
   parser.add_argument("OUTPUT_DIR", help="Output DIR")
   args = parser.parse_args()

이제 다음과 같이 IEF 데이터베이스의 존재를 확인하십시오.

directory = os.path.dirname(args.OUTPUT_CSV)

if not os.path.exists(directory):os.makedirs(directory)
if os.path.exists(args.IEF_DATABASE) and \ os.path.isfile(args.IEF_DATABASE):
   main(args.IEF_DATABASE, args.OUTPUT_CSV)
   else: print("Supplied input file {} does not exist or is not a " "file".format(args.IEF_DATABASE))
sys.exit(1)

이제 다음과 같이 SQLite 데이터베이스와 연결하여 커서를 통해 쿼리를 실행합니다.

def main(database, out_csv):
   print("[+] Connecting to SQLite database")
   conn = sqlite3.connect(database)
   c = conn.cursor()

다음 코드 줄을 사용하여 Yahoo 메일 연락처 캐시 레코드의 인스턴스를 가져올 수 있습니다.

print("Querying IEF database for Yahoo Contact Fragments from " "the Chrome Cache Records Table")
   try:
      c.execute("select * from 'Chrome Cache Records' where URL like " "'https://data.mail.yahoo.com" "/classicab/v2/contacts/?format=json%'")
   except sqlite3.OperationalError:
      print("Received an error querying the database --    database may be" "corrupt or not have a Chrome Cache Records table")
      sys.exit(2)

이제 위 쿼리에서 반환 된 튜플 목록은 다음과 같이 변수에 저장됩니다.

contact_cache = c.fetchall()
contact_data = process_contacts(contact_cache)
write_csv(contact_data, out_csv)

여기서는 두 가지 방법을 사용합니다. process_contacts() 결과 목록을 설정하고 각 연락처 캐시 레코드를 반복하고 json.loads() 추가 조작을 위해 테이블에서 추출한 JSON 데이터를 변수에 저장하려면-

def process_contacts(contact_cache):
   print("[+] Processing {} cache files matching Yahoo contact cache " " data".format(len(contact_cache)))
   results = []
   
   for contact in contact_cache:
      url = contact[0]
      first_visit = contact[1]
      last_visit = contact[2]
      last_sync = contact[3]
      loc = contact[8]
	   contact_json = json.loads(contact[7].decode())
      total_contacts = contact_json["total"]
      total_count = contact_json["count"]
      
      if "contacts" not in contact_json:
         continue
      for c in contact_json["contacts"]:
         name, anni, bday, emails, phones, links = ("", "", "", "", "", "")
            if "name" in c:
            name = c["name"]["givenName"] + " " + \ c["name"]["middleName"] + " " + c["name"]["familyName"]
            
            if "anniversary" in c:
            anni = c["anniversary"]["month"] + \"/" + c["anniversary"]["day"] + "/" + \c["anniversary"]["year"]
            
            if "birthday" in c:
            bday = c["birthday"]["month"] + "/" + \c["birthday"]["day"] + "/" + c["birthday"]["year"]
            
            if "emails" in c:
               emails = ', '.join([x["ep"] for x in c["emails"]])
            
            if "phones" in c:
               phones = ', '.join([x["ep"] for x in c["phones"]])
            
            if "links" in c:
              links = ', '.join([x["ep"] for x in c["links"]])

이제 회사, 제목 및 메모에 대해 다음과 같이 get 메서드가 사용됩니다.

company = c.get("company", "")
title = c.get("jobTitle", "")
notes = c.get("notes", "")

이제 다음과 같이 메타 데이터 목록과 추출 된 데이터 요소를 결과 목록에 추가하겠습니다.

results.append([url, first_visit, last_visit, last_sync, loc, name, bday,anni, emails, phones, links, company, title, notes,total_contacts, total_count])
return results

이제 CSV_Writer() 방법, 우리는 CSV 파일에 내용을 작성합니다-

def write_csv(data, output):
   print("[+] Writing {} contacts to {}".format(len(data), output))
   with open(output, "w", newline="") as csvfile:
      csv_writer = csv.writer(csvfile)
      csv_writer.writerow([
         "URL", "First Visit (UTC)", "Last Visit (UTC)",
         "Last Sync (UTC)", "Location", "Contact Name", "Bday",
         "Anniversary", "Emails", "Phones", "Links", "Company", "Title",
         "Notes", "Total Contacts", "Count of Contacts in Cache"])
      csv_writer.writerows(data)

위 스크립트의 도움으로 IEF 데이터베이스를 사용하여 Yahoo 메일에서 캐시 된 데이터를 처리 할 수 ​​있습니다.

이전 장에서는 Python을 사용하는 네트워크 포렌식의 몇 가지 개념을 다루었습니다. 이 장에서는 Python을 사용하는 네트워크 포렌식을 더 심층적으로 이해하겠습니다.

아름다운 수프를 사용한 웹 페이지 보존

World Wide Web (WWW)은 고유 한 정보 자원입니다. 그러나 그 유산은 놀라운 속도로 콘텐츠 손실로 인해 높은 위험에 처해 있습니다. 많은 문화 유산 및 학술 기관, 비영리 단체 및 민간 기업이 관련 문제를 조사하고 웹 아카이빙을위한 기술 솔루션 개발에 기여했습니다.

웹 페이지 보존 또는 웹 보관은 World Wide Web에서 데이터를 수집하여 데이터가 아카이브에 보존되도록하고 미래의 연구자, 역사가 및 대중이 사용할 수 있도록하는 프로세스입니다. 웹 페이지 보존에 대해 더 진행하기 전에 다음과 같이 웹 페이지 보존과 관련된 몇 가지 중요한 문제에 대해 논의하겠습니다.

  • Change in Web Resources − 웹 리소스는 매일 계속 변경되므로 웹 페이지 보존에 어려움이 있습니다.

  • Large Quantity of Resources − 웹 페이지 보존과 관련된 또 다른 문제는 보존해야 할 자원의 양이 많다는 것입니다.

  • Integrity − 웹 페이지는 무결성을 보호하기 위해 무단 수정, 삭제 또는 제거로부터 보호되어야합니다.

  • Dealing with multimedia data − 웹 페이지를 보존하는 동안 멀티미디어 데이터도 처리해야하는데 이로 인해 문제가 발생할 수 있습니다.

  • Providing access − 보존 외에도 웹 리소스에 대한 액세스를 제공하고 소유권 문제를 처리하는 문제도 해결되어야합니다.

이 장에서는 다음과 같은 Python 라이브러리를 사용합니다. Beautiful Soup 웹 페이지 보존을 위해.

뷰티플 스프 란?

Beautiful Soup은 HTML 및 XML 파일에서 데이터를 가져 오기위한 Python 라이브러리입니다. 함께 사용할 수 있습니다urlib웹 페이지 자체를 가져올 수 없기 때문에 수프 개체를 만들려면 입력 (문서 또는 URL)이 필요하기 때문입니다. 이에 대한 자세한 내용은 www.crummy.com/software/BeautifulSoup/bs4/doc/ 에서 확인할 수 있습니다.

사용하기 전에 다음 명령을 사용하여 타사 라이브러리를 설치해야합니다.

pip install bs4

다음으로 Anaconda 패키지 관리자를 사용하여 다음과 같이 Beautiful Soup을 설치할 수 있습니다.

conda install -c anaconda beautifulsoup4

웹 페이지 보존을위한 Python 스크립트

Beautiful Soup이라는 타사 라이브러리를 사용하여 웹 페이지를 보존하는 Python 스크립트는 여기에서 설명합니다.

먼저 다음과 같이 필요한 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse

from bs4 import BeautifulSoup, SoupStrainer
from datetime import datetime

import hashlib
import logging
import os
import ssl
import sys
from urllib.request import urlopen

import urllib.error
logger = logging.getLogger(__name__)

이 스크립트는 두 개의 위치 인수를 취할 것입니다. 하나는 보존 할 URL이고 다른 하나는 아래에 표시된대로 원하는 출력 디렉토리입니다.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Web Page preservation')
   parser.add_argument("DOMAIN", help="Website Domain")
   parser.add_argument("OUTPUT_DIR", help="Preservation Output Directory")
   parser.add_argument("-l", help="Log file path",
   default=__file__[:-3] + ".log")
   args = parser.parse_args()

이제 파일과 스트림 핸들러를 지정하여 스크립트에 대한 로깅을 설정하고 다음과 같이 수집 프로세스를 문서화합니다.

logger.setLevel(logging.DEBUG)
msg_fmt = logging.Formatter("%(asctime)-15s %(funcName)-10s""%(levelname)-8s %(message)s")
strhndl = logging.StreamHandler(sys.stderr)
strhndl.setFormatter(fmt=msg_fmt)
fhndl = logging.FileHandler(args.l, mode='a')
fhndl.setFormatter(fmt=msg_fmt)

logger.addHandler(strhndl)
logger.addHandler(fhndl)
logger.info("Starting BS Preservation")
logger.debug("Supplied arguments: {}".format(sys.argv[1:]))
logger.debug("System " + sys.platform)
logger.debug("Version " + sys.version)

이제 다음과 같이 원하는 출력 디렉토리에서 입력 유효성 검사를 수행해 보겠습니다.

if not os.path.exists(args.OUTPUT_DIR):
   os.makedirs(args.OUTPUT_DIR)
main(args.DOMAIN, args.OUTPUT_DIR)

이제 우리는 main() 다음과 같이 입력 URL에 대한 추가 유효성 검사와 함께 실제 이름 앞의 불필요한 요소를 제거하여 웹 사이트의 기본 이름을 추출하는 기능-

def main(website, output_dir):
   base_name = website.replace("https://", "").replace("http://", "").replace("www.", "")
   link_queue = set()
   
   if "http://" not in website and "https://" not in website:
      logger.error("Exiting preservation - invalid user input: {}".format(website))
      sys.exit(1)
   logger.info("Accessing {} webpage".format(website))
   context = ssl._create_unverified_context()

이제 urlopen () 메서드를 사용하여 URL과의 연결을 열어야합니다. 다음과 같이 try-except 블록을 사용합니다.

try:
   index = urlopen(website, context=context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   logger.error("Exiting preservation - unable to access page: {}".format(website))
   sys.exit(2)
logger.debug("Successfully accessed {}".format(website))

다음 코드 줄에는 아래 설명 된 세 가지 기능이 포함됩니다.

  • write_output() 출력 디렉토리에 첫 번째 웹 페이지를 작성하려면

  • find_links() 이 웹 페이지의 링크를 식별하는 기능

  • recurse_pages() 웹 페이지의 모든 링크를 반복하고 검색하는 기능입니다.

write_output(website, index, output_dir)
link_queue = find_links(base_name, index, link_queue)
logger.info("Found {} initial links on webpage".format(len(link_queue)))
recurse_pages(website, link_queue, context, output_dir)
logger.info("Completed preservation of {}".format(website))

이제 정의하겠습니다. write_output() 다음과 같이 방법-

def write_output(name, data, output_dir, counter=0):
   name = name.replace("http://", "").replace("https://", "").rstrip("//")
   directory = os.path.join(output_dir, os.path.dirname(name))
   
   if not os.path.exists(directory) and os.path.dirname(name) != "":
      os.makedirs(directory)

웹 페이지에 대한 몇 가지 세부 정보를 기록한 다음 다음을 사용하여 데이터의 해시를 기록해야합니다. hash_data() 다음과 같이 방법-

logger.debug("Writing {} to {}".format(name, output_dir)) logger.debug("Data Hash: {}".format(hash_data(data)))
path = os.path.join(output_dir, name)
path = path + "_" + str(counter)
with open(path, "w") as outfile:
   outfile.write(data)
logger.debug("Output File Hash: {}".format(hash_file(path)))

이제 정의 hash_data() 우리가 읽는 도움으로 방법 UTF-8 인코딩 된 데이터를 생성 한 다음 SHA-256 다음과 같이 해시-

def hash_data(data):
   sha256 = hashlib.sha256()
   sha256.update(data.encode("utf-8"))
   return sha256.hexdigest()
def hash_file(file):
   sha256 = hashlib.sha256()
   with open(file, "rb") as in_file:
      sha256.update(in_file.read())
return sha256.hexdigest()

이제 Beautifulsoup 아래의 웹 페이지 데이터에서 개체 find_links() 다음과 같이 방법-

def find_links(website, page, queue):
   for link in BeautifulSoup(page, "html.parser",parse_only = SoupStrainer("a", href = True)):
      if website in link.get("href"):
         if not os.path.basename(link.get("href")).startswith("#"):
            queue.add(link.get("href"))
   return queue

이제 정의해야합니다. recurse_pages() 다음과 같이 웹 사이트 URL, 현재 링크 대기열, 확인되지 않은 SSL 컨텍스트 및 출력 디렉토리의 입력을 제공하여 방법-

def recurse_pages(website, queue, context, output_dir):
   processed = []
   counter = 0
   
   while True:
      counter += 1
      if len(processed) == len(queue):
         break
      for link in queue.copy(): if link in processed:
         continue
	   processed.append(link)
      try:
      page = urlopen(link,      context=context).read().decode("utf-8")
      except urllib.error.HTTPError as e:
         msg = "Error accessing webpage: {}".format(link)
         logger.error(msg)
         continue

이제 다음과 같이 링크 이름, 페이지 데이터, 출력 디렉토리 및 카운터를 전달하여 파일에 액세스 한 각 웹 페이지의 출력을 작성합니다.

write_output(link, page, output_dir, counter)
queue = find_links(website, page, queue)
logger.info("Identified {} links throughout website".format(
   len(queue)))

이제 웹 사이트의 URL, 출력 디렉토리 및 로그 파일 경로를 제공하여이 스크립트를 실행하면 나중에 사용할 수있는 해당 웹 페이지에 대한 세부 정보를 얻을 수 있습니다.

바이러스 사냥

법의학 분석가, 보안 연구원 및 사고 응답자가 유용한 소프트웨어와 맬웨어의 차이점을 어떻게 이해할 수 있는지 궁금한 적이 있습니까? 해커에 의해 빠르게 생성되는 맬웨어에 대해 연구하지 않고는 연구원과 전문가가 유용한 소프트웨어와 맬웨어의 차이점을 구분하는 것이 불가능하기 때문에 대답은 질문 자체에 있습니다. 이 섹션에서는 다음에 대해 논의하겠습니다.VirusShare,이 작업을 수행하는 도구입니다.

VirusShare 이해

VirusShare는 보안 연구원, 사고 대응 자 및 포렌식 분석가에게 라이브 악성 코드 샘플을 제공하는 개인 소유의 가장 큰 맬웨어 샘플 모음입니다. 3 천만 개 이상의 샘플이 포함되어 있습니다.

VirusShare의 이점은 무료로 사용할 수있는 맬웨어 해시 목록입니다. 누구나 이러한 해시를 사용하여 매우 포괄적 인 해시 세트를 만들고이를 사용하여 잠재적 인 악성 파일을 식별 할 수 있습니다. 그러나 VirusShare를 사용하기 전에 다음을 방문하는 것이 좋습니다.https://virusshare.com 상세 사항은.

Python을 사용하여 VirusShare에서 줄 바꿈으로 구분 된 해시 목록 만들기

VirusShare의 해시 목록은 X-way 및 EnCase와 같은 다양한 포렌식 도구에서 사용할 수 있습니다. 아래에 설명 된 스크립트에서 VirusShare에서 해시 목록을 자동으로 다운로드하여 줄 바꿈으로 구분 된 해시 목록을 만들 것입니다.

이 스크립트에는 타사 Python 라이브러리가 필요합니다. tqdm 다음과 같이 다운로드 할 수 있습니다-

pip install tqdm

이 스크립트에서 먼저 VirusShare 해시 페이지를 읽고 가장 최근의 해시 목록을 동적으로 식별합니다. 그런 다음 진행률 표시 줄을 초기화하고 원하는 범위의 해시 목록을 다운로드합니다.

먼저 다음 라이브러리를 가져옵니다.

from __future__ import print_function

import argparse
import os
import ssl
import sys
import tqdm

from urllib.request import urlopen
import urllib.error

이 스크립트는 해시 세트에 대한 원하는 경로가 될 하나의 위치 인수를 사용합니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Hash set from VirusShare')
   parser.add_argument("OUTPUT_HASH", help = "Output Hashset")
   parser.add_argument("--start", type = int, help = "Optional starting location")
   args = parser.parse_args()

이제 다음과 같이 표준 입력 유효성 검사를 수행합니다.

directory = os.path.dirname(args.OUTPUT_HASH)
if not os.path.exists(directory):
   os.makedirs(directory)
if args.start:
   main(args.OUTPUT_HASH, start=args.start)
else:
   main(args.OUTPUT_HASH)

이제 정의해야합니다. main() 기능 **kwargs 이것은 딕셔너리를 생성 할 것이기 때문에 아래와 같이 제공된 키 인자를 지원할 수 있습니다.

def main(hashset, **kwargs):
   url = "https://virusshare.com/hashes.4n6"
   print("[+] Identifying hash set range from {}".format(url))
   context = ssl._create_unverified_context()

이제 다음을 사용하여 VirusShare 해시 페이지를 열어야합니다. urlib.request.urlopen()방법. 다음과 같이 try-except 블록을 사용합니다.

try:
   index = urlopen(url, context = context).read().decode("utf-8")
except urllib.error.HTTPError as e:
   print("[-] Error accessing webpage - exiting..")
   sys.exit(1)

이제 다운로드 한 페이지에서 최신 해시 목록을 식별합니다. HTML의 마지막 인스턴스를 찾아이를 수행 할 수 있습니다.hrefVirusShare 해시 목록에 태그를 추가합니다. 다음 코드 라인으로 수행 할 수 있습니다.

tag = index.rfind(r'a href = "hashes/VirusShare_')
stop = int(index[tag + 27: tag + 27 + 5].lstrip("0"))

if "start" not in kwa<rgs:
   start = 0
else:
   start = kwargs["start"]

if start < 0 or start > stop:
   print("[-] Supplied start argument must be greater than or equal ""to zero but less than the latest hash list, ""currently: {}".format(stop))
sys.exit(2)
print("[+] Creating a hashset from hash lists {} to {}".format(start, stop))
hashes_downloaded = 0

이제 우리는 tqdm.trange() 다음과 같이 루프 및 진행률 표시 줄을 만드는 방법-

for x in tqdm.trange(start, stop + 1, unit_scale=True,desc="Progress"):
   url_hash = "https://virusshare.com/hashes/VirusShare_"\"{}.md5".format(str(x).zfill(5))
   try:
      hashes = urlopen(url_hash, context=context).read().decode("utf-8")
      hashes_list = hashes.split("\n")
   except urllib.error.HTTPError as e:
      print("[-] Error accessing webpage for hash list {}"" - continuing..".format(x))
   continue

위의 단계를 성공적으로 수행 한 후 a + 모드에서 해시 세트 텍스트 파일을 열어 텍스트 파일의 하단에 추가합니다.

with open(hashset, "a+") as hashfile:
   for line in hashes_list:
   if not line.startswith("#") and line != "":
      hashes_downloaded += 1
      hashfile.write(line + '\n')
   print("[+] Finished downloading {} hashes into {}".format(
      hashes_downloaded, hashset))

위의 스크립트를 실행하면 텍스트 형식의 MD5 해시 값이 포함 된 최신 해시 목록이 표시됩니다.

이전 장에서는 네트워크 포렌식의 중요성과 프로세스 및 관련 개념에 대해 논의했습니다. 이 장에서는 디지털 포렌식에서 이메일의 역할과 Python을 사용한 조사에 대해 알아 보겠습니다.

조사에서 이메일의 역할

이메일은 비즈니스 커뮤니케이션에서 매우 중요한 역할을하며 인터넷에서 가장 중요한 애플리케이션 중 하나로 부상했습니다. 컴퓨터뿐만 아니라 휴대폰 및 태블릿과 같은 다른 전자 장치에서도 메시지와 문서를 보내는 편리한 모드입니다.

이메일의 부정적인 측면은 범죄자들이 회사에 대한 중요한 정보를 유출 할 수 있다는 것입니다. 따라서 최근 몇 년 동안 디지털 포렌식에서 이메일의 역할이 증가했습니다. 디지털 포렌식에서 이메일은 중요한 증거로 간주되며 이메일 헤더 분석은 포렌식 프로세스 중에 증거를 수집하는 데 중요해졌습니다.

조사자는 이메일 포렌식을 수행하는 동안 다음과 같은 목표를 가지고 있습니다.

  • 주요 범죄자를 식별하려면
  • 필요한 증거를 수집하려면
  • 연구 결과 발표
  • 케이스를 구축하려면

이메일 포렌식의 당면 과제

이메일 포렌식은 현재 대부분의 커뮤니케이션이 이메일에 의존하기 때문에 조사에서 매우 중요한 역할을합니다. 그러나 이메일 포렌식 조사관은 조사 중에 다음과 같은 문제에 직면 할 수 있습니다.

가짜 이메일

이메일 포렌식에서 가장 큰 문제는 헤더를 조작하고 스크립팅하여 만든 가짜 이메일을 사용하는 것입니다.이 범주에서 범죄자들은 ​​또한 등록 된 사용자가 만료 된 임시 주소로 이메일을받을 수있는 서비스 인 임시 이메일을 사용합니다. 일정 기간 후.

스푸핑

이메일 포렌식의 또 다른 문제는 범죄자가 이메일을 다른 사람의 것으로 제시하는 데 사용되는 스푸핑입니다. 이 경우 기기는 가짜 IP 주소와 원래 IP 주소를 모두 수신합니다.

익명의 재 메일 링

여기서 이메일 서버는 이메일 메시지를 추가로 전달하기 전에 이메일 메시지에서 식별 정보를 제거합니다. 이것은 이메일 조사에 또 다른 큰 도전으로 이어집니다.

이메일 포렌식 조사에 사용되는 기술

이메일 포렌식은 전송 날짜 / 시간 및 보낸 사람의 의도와 같은 기타 정보와 함께 메시지의 실제 발신자와 수신자를 식별하기위한 증거로서 이메일의 출처 및 내용을 연구합니다. 메타 데이터 조사, 포트 스캔 및 키워드 검색이 포함됩니다.

이메일 포렌식 조사에 사용할 수있는 몇 가지 일반적인 기술은 다음과 같습니다.

  • 헤더 분석
  • 서버 조사
  • 네트워크 장치 조사
  • 발신자 메일러 지문
  • 소프트웨어 임베디드 식별자

다음 섹션에서는 이메일 조사를 위해 Python을 사용하여 정보를 가져 오는 방법을 배웁니다.

EML 파일에서 정보 추출

EML 파일은 기본적으로 이메일 메시지를 저장하는 데 널리 사용되는 파일 형식의 이메일입니다. Microsoft Outlook, Outlook Express 및 Windows Live Mail과 같은 여러 이메일 클라이언트에서 호환되는 구조화 된 텍스트 파일입니다.

EML 파일은 이메일 헤더, 본문 내용, 첨부 파일 데이터를 일반 텍스트로 저장합니다. 이진 데이터를 인코딩하는 데 base64를 사용하고 콘텐츠 정보를 저장하기 위해 QP (Quoted-Printable) 인코딩을 사용합니다. EML 파일에서 정보를 추출하는 데 사용할 수있는 Python 스크립트는 다음과 같습니다.

먼저 아래와 같이 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser, FileType
from email import message_from_file

import os
import quopri
import base64

위의 라이브러리에서 quopriEML 파일에서 QP 인코딩 값을 디코딩하는 데 사용됩니다. base64로 인코딩 된 데이터는 다음을 사용하여 디코딩 할 수 있습니다.base64 도서관.

다음으로 명령 줄 처리기에 대한 인수를 제공하겠습니다. 여기서는 아래와 같이 EML 파일의 경로가되는 하나의 인수 만 허용합니다.

if __name__ == '__main__':
   parser = ArgumentParser('Extracting information from EML file')
   parser.add_argument("EML_FILE",help="Path to EML File", type=FileType('r'))
   args = parser.parse_args()
   main(args.EML_FILE)

이제 정의해야합니다. main() 이름이 지정된 메소드를 사용할 함수 message_from_file()이메일 라이브러리에서 객체와 같은 파일을 읽습니다. 여기서는 이름이 지정된 결과 변수를 사용하여 헤더, 본문 내용, 첨부 파일 및 기타 페이로드 정보에 액세스합니다.emlfile 아래에 주어진 코드와 같이-

def main(input_file):
   emlfile = message_from_file(input_file)
   for key, value in emlfile._headers:
      print("{}: {}".format(key, value))
print("\nBody\n")

if emlfile.is_multipart():
   for part in emlfile.get_payload():
      process_payload(part)
else:
   process_payload(emlfile[1])

이제 정의해야합니다. process_payload() 사용하여 메시지 본문 내용을 추출하는 방법 get_payload()방법. 다음을 사용하여 QP 인코딩 된 데이터를 디코딩합니다.quopri.decodestring()함수. 또한 콘텐츠 MIME 유형을 확인하여 이메일 저장을 제대로 처리 할 수 ​​있도록합니다. 아래 주어진 코드를 관찰하십시오-

def process_payload(payload):
   print(payload.get_content_type() + "\n" + "=" * len(payload.get_content_type()))
   body = quopri.decodestring(payload.get_payload())
   
   if payload.get_charset():
      body = body.decode(payload.get_charset())
else:
   try:
      body = body.decode()
   except UnicodeDecodeError:
      body = body.decode('cp1252')

if payload.get_content_type() == "text/html":
   outfile = os.path.basename(args.EML_FILE.name) + ".html"
   open(outfile, 'w').write(body)
elif payload.get_content_type().startswith('application'):
   outfile = open(payload.get_filename(), 'wb')
   body = base64.b64decode(payload.get_payload())
   outfile.write(body)
   outfile.close()
   print("Exported: {}\n".format(outfile.name))
else:
   print(body)

위의 스크립트를 실행하면 콘솔에서 다양한 페이로드와 함께 헤더 정보를 얻을 수 있습니다.

Python을 사용하여 MSG 파일 분석

이메일 메시지는 다양한 형식으로 제공됩니다. MSG는 Microsoft Outlook 및 Exchange에서 사용되는 이러한 종류의 형식 중 하나입니다. MSG 확장자를 가진 파일에는 헤더 및 기본 메시지 본문, 하이퍼 링크 및 첨부 파일에 대한 일반 ASCII 텍스트가 포함될 수 있습니다.

이 섹션에서는 Outlook API를 사용하여 MSG 파일에서 정보를 추출하는 방법을 배웁니다. 다음 Python 스크립트는 Windows에서만 작동합니다. 이를 위해 타사 Python 라이브러리를 설치해야합니다.pywin32 다음과 같이-

pip install pywin32

이제 표시된 명령을 사용하여 다음 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser

import os
import win32com.client
import pywintypes

이제 명령 줄 처리기에 대한 인수를 제공하겠습니다. 여기서 두 개의 인수 하나는 MSG 파일의 경로이고 다른 하나는 다음과 같이 원하는 출력 폴더입니다.

if __name__ == '__main__':
   parser = ArgumentParser(‘Extracting information from MSG file’)
   parser.add_argument("MSG_FILE", help="Path to MSG file")
   parser.add_argument("OUTPUT_DIR", help="Path to output folder")
   args = parser.parse_args()
   out_dir = args.OUTPUT_DIR
   
   if not os.path.exists(out_dir):
      os.makedirs(out_dir)
   main(args.MSG_FILE, args.OUTPUT_DIR)

이제 정의해야합니다. main() 우리가 호출 할 함수 win32com 설정을위한 라이브러리 Outlook API 추가로 액세스를 허용합니다. MAPI 네임 스페이스.

def main(msg_file, output_dir):
   mapi = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
   msg = mapi.OpenSharedItem(os.path.abspath(args.MSG_FILE))
   
   display_msg_attribs(msg)
   display_msg_recipients(msg)
   
   extract_msg_body(msg, output_dir)
   extract_attachments(msg, output_dir)

이제이 스크립트에서 사용중인 다른 기능을 정의하십시오. 아래에 주어진 코드는display_msg_attribs() 제목,받는 사람, 숨은 참조, 참조, 크기, 보낸 사람 이름, 보낸 사람 등과 같은 메시지의 다양한 속성을 표시 할 수있는 기능입니다.

def display_msg_attribs(msg):
   attribs = [
      'Application', 'AutoForwarded', 'BCC', 'CC', 'Class',
      'ConversationID', 'ConversationTopic', 'CreationTime',
      'ExpiryTime', 'Importance', 'InternetCodePage', 'IsMarkedAsTask',
      'LastModificationTime', 'Links','ReceivedTime', 'ReminderSet',
      'ReminderTime', 'ReplyRecipientNames', 'Saved', 'Sender',
      'SenderEmailAddress', 'SenderEmailType', 'SenderName', 'Sent',
      'SentOn', 'SentOnBehalfOfName', 'Size', 'Subject',
      'TaskCompletedDate', 'TaskDueDate', 'To', 'UnRead'
   ]
   print("\nMessage Attributes")
   for entry in attribs:
      print("{}: {}".format(entry, getattr(msg, entry, 'N/A')))

이제 display_msg_recipeints() 메시지를 반복하고 수신자 세부 정보를 표시하는 기능입니다.

def display_msg_recipients(msg):
   recipient_attrib = ['Address', 'AutoResponse', 'Name', 'Resolved', 'Sendable']
   i = 1
   
   while True:
   try:
      recipient = msg.Recipients(i)
   except pywintypes.com_error:
      break
   print("\nRecipient {}".format(i))
   print("=" * 15)
   
   for entry in recipient_attrib:
      print("{}: {}".format(entry, getattr(recipient, entry, 'N/A')))
   i += 1

다음으로 우리는 extract_msg_body() 메시지에서 본문 내용, HTML 및 일반 텍스트를 추출하는 함수입니다.

def extract_msg_body(msg, out_dir):
   html_data = msg.HTMLBody.encode('cp1252')
   outfile = os.path.join(out_dir, os.path.basename(args.MSG_FILE))
   
   open(outfile + ".body.html", 'wb').write(html_data)
   print("Exported: {}".format(outfile + ".body.html"))
   body_data = msg.Body.encode('cp1252')
   
   open(outfile + ".body.txt", 'wb').write(body_data)
   print("Exported: {}".format(outfile + ".body.txt"))

다음으로 우리는 extract_attachments() 첨부 데이터를 원하는 출력 디렉토리로 내보내는 기능.

def extract_attachments(msg, out_dir):
   attachment_attribs = ['DisplayName', 'FileName', 'PathName', 'Position', 'Size']
   i = 1 # Attachments start at 1
   
   while True:
      try:
         attachment = msg.Attachments(i)
   except pywintypes.com_error:
      break

모든 기능이 정의되면 다음 코드 줄을 사용하여 모든 속성을 콘솔에 인쇄합니다.

print("\nAttachment {}".format(i))
print("=" * 15)
   
for entry in attachment_attribs:
   print('{}: {}'.format(entry, getattr(attachment, entry,"N/A")))
outfile = os.path.join(os.path.abspath(out_dir),os.path.split(args.MSG_FILE)[-1])
   
if not os.path.exists(outfile):
os.makedirs(outfile)
outfile = os.path.join(outfile, attachment.FileName)
attachment.SaveAsFile(outfile)
   
print("Exported: {}".format(outfile))
i += 1

위의 스크립트를 실행 한 후 콘솔 창에서 출력 디렉토리의 여러 파일과 함께 메시지 및 첨부 파일의 속성을 가져옵니다.

Python을 사용하여 Google 테이크 아웃에서 MBOX 파일 구조화

MBOX 파일은 저장된 메시지를 분할하는 특수 형식의 텍스트 파일입니다. 종종 UNIX 시스템, Thunderbolt 및 Google 테이크 아웃과 관련하여 발견됩니다.

이 섹션에서는 Google 테이크 아웃에서 가져온 MBOX 파일을 구성하는 Python 스크립트를 볼 수 있습니다. 그러나 그 전에 Google 계정 또는 Gmail 계정을 사용하여 이러한 MBOX 파일을 생성하는 방법을 알아야합니다.

MBX 형식으로 Google 계정 사서함 가져 오기

Google 계정 사서함을 얻는 것은 Gmail 계정을 백업하는 것을 의미합니다. 다양한 개인적 또는 직업적 이유로 백업을 수행 할 수 있습니다. Google은 Gmail 데이터 백업을 제공합니다. Google 계정 사서함을 MBOX 형식으로 얻으려면 아래 단계를 따라야합니다.

  • 열다 My account 계기반.

  • 개인 정보 및 개인 정보 섹션으로 이동하여 콘텐츠 링크 제어를 선택합니다.

  • 새 아카이브를 생성하거나 기존 아카이브를 관리 할 수 ​​있습니다. 클릭하면CREATE ARCHIVE 링크를 클릭하면 포함하려는 각 Google 제품에 대한 몇 가지 확인란이 표시됩니다.

  • 제품을 선택한 후 목록에서 선택할 수있는 전달 방법과 함께 아카이브의 파일 유형과 최대 크기를 자유롭게 선택할 수 있습니다.

  • 마지막으로이 백업을 MBOX 형식으로 가져옵니다.

Python 코드

이제 위에서 설명한 MBOX 파일은 아래와 같이 Python을 사용하여 구조화 할 수 있습니다.

먼저 다음과 같이 Python 라이브러리를 가져와야합니다.

from __future__ import print_function
from argparse import ArgumentParser

import mailbox
import os
import time
import csv
from tqdm import tqdm

import base64

모든 라이브러리는 이전 스크립트에서 사용 및 설명되었습니다. mailbox MBOX 파일을 구문 분석하는 데 사용되는 라이브러리.

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 하나는 MBOX 파일의 경로이고 다른 하나는 원하는 출력 폴더입니다.

if __name__ == '__main__':
   parser = ArgumentParser('Parsing MBOX files')
   parser.add_argument("MBOX", help="Path to mbox file")
   parser.add_argument(
      "OUTPUT_DIR",help = "Path to output directory to write report ""and exported content")
   args = parser.parse_args()
   main(args.MBOX, args.OUTPUT_DIR)

이제 정의합니다 main() 기능 및 호출 mbox 경로를 제공하여 MBOX 파일을 구문 분석 할 수있는 메일 함 라이브러리 클래스-

def main(mbox_file, output_dir):
   print("Reading mbox file")
   mbox = mailbox.mbox(mbox_file, factory=custom_reader)
   print("{} messages to parse".format(len(mbox)))

이제 독자 방법을 정의하십시오. mailbox 다음과 같이 라이브러리-

def custom_reader(data_stream):
   data = data_stream.read()
   try:
      content = data.decode("ascii")
   except (UnicodeDecodeError, UnicodeEncodeError) as e:
      content = data.decode("cp1252", errors="replace")
   return mailbox.mboxMessage(content)

이제 다음과 같이 추가 처리를 위해 몇 가지 변수를 만듭니다.

parsed_data = []
attachments_dir = os.path.join(output_dir, "attachments")

if not os.path.exists(attachments_dir):
   os.makedirs(attachments_dir)
columns = [
   "Date", "From", "To", "Subject", "X-Gmail-Labels", "Return-Path", "Received", 
   "Content-Type", "Message-ID","X-GM-THRID", "num_attachments_exported", "export_path"]

다음으로 tqdm 진행률 표시 줄을 생성하고 다음과 같이 반복 프로세스를 추적합니다.

for message in tqdm(mbox):
   msg_data = dict()
   header_data = dict(message._headers)
for hdr in columns:
   msg_data[hdr] = header_data.get(hdr, "N/A")

이제 날씨 메시지에 페이로드가 있는지 확인하십시오. 그렇다면 우리는 정의 할 것입니다write_payload() 다음과 같이 방법-

if len(message.get_payload()):
   export_path = write_payload(message, attachments_dir)
   msg_data['num_attachments_exported'] = len(export_path)
   msg_data['export_path'] = ", ".join(export_path)

이제 데이터를 추가해야합니다. 그런 다음 우리는create_report() 다음과 같이 방법-

parsed_data.append(msg_data)
create_report(
   parsed_data, os.path.join(output_dir, "mbox_report.csv"), columns)
def write_payload(msg, out_dir):
   pyld = msg.get_payload()
   export_path = []
   
if msg.is_multipart():
   for entry in pyld:
      export_path += write_payload(entry, out_dir)
else:
   content_type = msg.get_content_type()
   if "application/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "image/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))

   elif "video/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "audio/" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "text/csv" in content_type.lower():
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "info/" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/calendar" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   elif "text/rtf" in content_type.lower():
      export_path.append(export_content(msg, out_dir,
      msg.get_payload()))
   else:
      if "name=" in msg.get('Content-Disposition', "N/A"):
         content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
   elif "name=" in msg.get('Content-Type', "N/A"):
      content = base64.b64decode(msg.get_payload())
      export_path.append(export_content(msg, out_dir, content))
return export_path

위의 if-else 문은 이해하기 쉽습니다. 이제 파일 이름을 추출 할 메서드를 정의해야합니다.msg 다음과 같이 개체-

def export_content(msg, out_dir, content_data):
   file_name = get_filename(msg)
   file_ext = "FILE"
   
   if "." in file_name: file_ext = file_name.rsplit(".", 1)[-1]
   file_name = "{}_{:.4f}.{}".format(file_name.rsplit(".", 1)[0], time.time(), file_ext)
   file_name = os.path.join(out_dir, file_name)

이제 다음 코드 줄의 도움으로 실제로 파일을 내보낼 수 있습니다.

if isinstance(content_data, str):
   open(file_name, 'w').write(content_data)
else:
   open(file_name, 'wb').write(content_data)
return file_name

이제 파일 이름을 추출하는 함수를 정의하겠습니다. message 다음과 같이 이러한 파일의 이름을 정확하게 표현하려면-

def get_filename(msg):
   if 'name=' in msg.get("Content-Disposition", "N/A"):
      fname_data = msg["Content-Disposition"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   elif 'name=' in msg.get("Content-Type", "N/A"):
      fname_data = msg["Content-Type"].replace("\r\n", " ")
      fname = [x for x in fname_data.split("; ") if 'name=' in x]
      file_name = fname[0].split("=", 1)[-1]
   else:
      file_name = "NO_FILENAME"
   fchars = [x for x in file_name if x.isalnum() or x.isspace() or x == "."]
   return "".join(fchars)

이제 다음을 정의하여 CSV 파일을 작성할 수 있습니다. create_report() 다음과 같이 기능-

def create_report(output_data, output_file, columns):
   with open(output_file, 'w', newline="") as outfile:
      csvfile = csv.DictWriter(outfile, columns)
      csvfile.writeheader()
      csvfile.writerows(output_data)

위에 제공된 스크립트를 실행하면 첨부 파일로 가득 찬 CSV 보고서와 디렉토리를 얻을 수 있습니다.

이 장에서는 Microsoft Windows 포렌식과 관련된 다양한 개념과 조사자가 조사 프로세스에서 얻을 수있는 중요한 아티팩트에 대해 설명합니다.

소개

아티팩트는 컴퓨터 사용자가 수행 한 활동과 관련된 중요한 정보가있는 컴퓨터 시스템 내의 개체 또는 영역입니다. 이 정보의 유형과 위치는 운영 체제에 따라 다릅니다. 법의학 분석 중에 이러한 인공물은 조사자의 관찰을 승인하거나 비 승인하는 데 매우 중요한 역할을합니다.

포렌식을위한 Windows 아티팩트의 중요성

Windows 아티팩트는 다음과 같은 이유로 중요성을 가정합니다.

  • 전 세계 트래픽의 약 90 %는 Windows를 운영 체제로 사용하는 컴퓨터에서 발생합니다. 그렇기 때문에 디지털 포렌식 심사관에게는 Windows 아티팩트가 매우 중요합니다.

  • Windows 운영 체제는 컴퓨터 시스템의 사용자 활동과 관련된 다양한 유형의 증거를 저장합니다. 이것은 디지털 포렌식에서 Windows 아티팩트의 중요성을 보여주는 또 다른 이유입니다.

  • 수사관은 사용자가 생성 한 데이터와 같은 기존 및 기존 영역을 중심으로 조사를 진행합니다. Windows 아티팩트는 시스템 생성 데이터 또는 아티팩트와 같은 비 전통적인 영역에 대한 조사를 이끌 수 있습니다.

  • Windows는 조사자뿐만 아니라 비공식 조사를 수행하는 기업과 개인에게도 도움이되는 풍부한 인공물을 제공합니다.

  • 최근 몇 년간 사이버 범죄의 증가는 Windows 아티팩트가 중요한 또 다른 이유입니다.

Windows 아티팩트 및 해당 Python 스크립트

이 섹션에서는 정보를 가져 오는 일부 Windows 아티팩트 및 Python 스크립트에 대해 설명합니다.

쓰레기통

포렌식 조사를위한 중요한 Windows 아티팩트 중 하나입니다. Windows 휴지통에는 사용자가 삭제했지만 아직 시스템에서 실제로 제거하지 않은 파일이 포함되어 있습니다. 사용자가 시스템에서 파일을 완전히 제거하더라도 중요한 조사 소스 역할을합니다. 이는 검사관이 삭제 된 파일에서 원본 파일 경로, 휴지통으로 보낸 시간과 같은 중요한 정보를 추출 할 수 있기 때문입니다.

휴지통 증거의 저장은 Windows 버전에 따라 다릅니다. 다음 Python 스크립트에서 두 개의 파일을 생성하는 Windows 7을 다룰 것입니다.$R 재활용 된 파일의 실제 내용을 포함하는 파일 및 $I 원본 파일 이름, 경로, 파일 삭제시 파일 크기가 포함 된 파일.

Python 스크립트의 경우 타사 모듈을 설치해야합니다. pytsk3, pyewfunicodecsv. 우리는 사용할 수 있습니다pip설치합니다. 다음 단계에 따라 휴지통에서 정보를 추출 할 수 있습니다.

  • 먼저 재귀 적 방법을 사용하여 $Recycle.bin 폴더로 시작하는 모든 파일을 선택하십시오. $I.

  • 다음으로 파일의 내용을 읽고 사용 가능한 메타 데이터 구조를 구문 분석합니다.

  • 이제 관련 $ R 파일을 검색합니다.

  • 마지막으로 검토를 위해 결과를 CSV 파일에 기록합니다.

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져와야합니다.

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import os
import struct

from utility.pytskutil import TSKUtil
import unicodecsv as csv

다음으로, 명령 줄 처리기에 대한 인수를 제공해야합니다. 여기서는 세 가지 인수를 허용합니다. 첫 번째는 증거 파일의 경로, 두 번째는 증거 파일의 유형, 세 번째는 CSV 보고서에 대한 원하는 출력 경로입니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Recycle Bin evidences')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
   choices = ('ewf', 'raw'))
   parser.add_argument('CSV_REPORT', help = "Path to CSV report")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)

이제 main()모든 처리를 처리 할 함수. 검색합니다$I 다음과 같이 파일-

def main(evidence, image_type, report_file):
   tsk_util = TSKUtil(evidence, image_type)
   dollar_i_files = tsk_util.recurse_files("$I", path = '/$Recycle.bin',logic = "startswith") if dollar_i_files is not None: processed_files = process_dollar_i(tsk_util, dollar_i_files) write_csv(report_file,['file_path', 'file_size', 'deleted_time','dollar_i_file', 'dollar_r_file', 'is_directory'],processed_files) else: print("No $I files found")

이제 우리가 발견하면 $I 파일을 보내야합니다. process_dollar_i() 받아 들일 기능 tsk_util 개체뿐만 아니라 $I 아래와 같이 파일-

def process_dollar_i(tsk_util, dollar_i_files):
   processed_files = []
   
   for dollar_i in dollar_i_files:
      file_attribs = read_dollar_i(dollar_i[2])
      if file_attribs is None:
         continue
      file_attribs['dollar_i_file'] = os.path.join('/$Recycle.bin', dollar_i[1][1:])

이제 다음과 같이 $ R 파일을 검색하십시오.

recycle_file_path = os.path.join('/$Recycle.bin',dollar_i[1].rsplit("/", 1)[0][1:]) dollar_r_files = tsk_util.recurse_files( "$R" + dollar_i[0][2:],path = recycle_file_path, logic = "startswith")
   
   if dollar_r_files is None:
      dollar_r_dir = os.path.join(recycle_file_path,"$R" + dollar_i[0][2:])
      dollar_r_dirs = tsk_util.query_directory(dollar_r_dir)
   
   if dollar_r_dirs is None:
      file_attribs['dollar_r_file'] = "Not Found"
      file_attribs['is_directory'] = 'Unknown'
   
   else:
      file_attribs['dollar_r_file'] = dollar_r_dir
      file_attribs['is_directory'] = True
   
   else:
      dollar_r = [os.path.join(recycle_file_path, r[1][1:])for r in dollar_r_files]
      file_attribs['dollar_r_file'] = ";".join(dollar_r)
      file_attribs['is_directory'] = False
      processed_files.append(file_attribs)
   return processed_files

이제 정의 read_dollar_i() 읽는 방법 $I즉, 메타 데이터를 구문 분석합니다. 우리는 사용할 것입니다read_random()서명의 처음 8 바이트를 읽는 메서드. 서명이 일치하지 않으면 아무것도 반환하지 않습니다. 그 후, 우리는 값을 읽고 압축을 풀어야합니다.$I 유효한 파일이면 file.

def read_dollar_i(file_obj):
   if file_obj.read_random(0, 8) != '\x01\x00\x00\x00\x00\x00\x00\x00':
      return None
   raw_file_size = struct.unpack('<q', file_obj.read_random(8, 8))
   raw_deleted_time = struct.unpack('<q',   file_obj.read_random(16, 8))
   raw_file_path = file_obj.read_random(24, 520)

이제 이러한 파일을 추출한 후 다음을 사용하여 정수를 사람이 읽을 수있는 값으로 해석해야합니다. sizeof_fmt() 아래 표시된 기능-

file_size = sizeof_fmt(raw_file_size[0])
deleted_time = parse_windows_filetime(raw_deleted_time[0])

file_path = raw_file_path.decode("utf16").strip("\x00")
return {'file_size': file_size, 'file_path': file_path,'deleted_time': deleted_time}

이제 정의해야합니다. sizeof_fmt() 다음과 같이 기능-

def sizeof_fmt(num, suffix = 'B'):
   for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
      if abs(num) < 1024.0:
         return "%3.1f%s%s" % (num, unit, suffix)
      num /= 1024.0
   return "%.1f%s%s" % (num, 'Yi', suffix)

이제 다음과 같이 형식화 된 날짜 및 시간으로 해석 된 정수에 대한 함수를 정의하십시오.

def parse_windows_filetime(date_value):
   microseconds = float(date_value) / 10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(
      microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

이제 우리는 write_csv() 처리 된 결과를 다음과 같이 CSV 파일에 쓰는 방법-

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

위의 스크립트를 실행하면 $ I 및 $ R 파일에서 데이터를 가져옵니다.

스티커 메모

Windows 스티커 메모는 실제 쓰기 습관을 펜과 종이로 대체합니다. 이러한 메모는 색상, 글꼴 등에 대한 다양한 옵션을 사용하여 바탕 화면에 떠 다니는 데 사용되었습니다. Windows 7에서 스티커 메모 파일은 OLE 파일로 저장되므로 다음 Python 스크립트에서이 OLE 파일을 조사하여 스티커 메모에서 메타 데이터를 추출합니다.

이 Python 스크립트의 경우 타사 모듈을 설치해야합니다. olefile, pytsk3, pyewf및 unicodecsv. 우리는 명령을 사용할 수 있습니다pip 설치합니다.

스티커 메모 파일에서 정보를 추출하기 위해 아래에 설명 된 단계를 따를 수 있습니다. StickyNote.sn

  • 먼저 증거 파일을 열고 모든 StickyNote.snt 파일을 찾습니다.

  • 그런 다음 OLE 스트림에서 메타 데이터 및 콘텐츠를 구문 분석하고 RTF 콘텐츠를 파일에 씁니다.

  • 마지막으로이 메타 데이터에 대한 CSV 보고서를 만듭니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser

import unicodecsv as csv
import os
import StringIO

from utility.pytskutil import TSKUtil
import olefile

다음으로,이 스크립트에서 사용될 전역 변수를 정의하십시오-

REPORT_COLS = ['note_id', 'created', 'modified', 'note_text', 'note_file']

다음으로, 명령 줄 처리기에 대한 인수를 제공해야합니다. 여기서는 다음과 같이 세 가지 인수를 허용합니다. 첫 번째는 증거 파일의 경로, 두 번째는 증거 파일의 유형, 세 번째는 다음과 같이 원하는 출력 경로입니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Sticky Notes')
   parser.add_argument('EVIDENCE_FILE', help="Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help="Evidence file format",choices=('ewf', 'raw'))
   parser.add_argument('REPORT_FOLDER', help="Path to report folder")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT_FOLDER)

이제 우리는 main() 아래에 표시된 이전 스크립트와 유사한 함수-

def main(evidence, image_type, report_folder):
   tsk_util = TSKUtil(evidence, image_type)
   note_files = tsk_util.recurse_files('StickyNotes.snt', '/Users','equals')

이제 결과 파일을 반복 해 보겠습니다. 그런 다음 우리는parse_snt_file() 함수를 사용하여 파일을 처리 한 다음 RTF 파일을 write_note_rtf() 다음과 같이 방법-

report_details = []
for note_file in note_files:
   user_dir = note_file[1].split("/")[1]
   file_like_obj = create_file_like_obj(note_file[2])
   note_data = parse_snt_file(file_like_obj)
   
   if note_data is None:
      continue
   write_note_rtf(note_data, os.path.join(report_folder, user_dir))
   report_details += prep_note_report(note_data, REPORT_COLS,"/Users" + note_file[1])
   write_csv(os.path.join(report_folder, 'sticky_notes.csv'), REPORT_COLS,report_details)

다음으로이 스크립트에서 사용되는 다양한 함수를 정의해야합니다.

우선 우리는 create_file_like_obj() 파일 크기를 읽는 기능 pytsk파일 객체. 그런 다음 정의합니다parse_snt_file() 파일 류 객체를 입력으로 받아들이고 스티커 메모 파일을 읽고 해석하는 데 사용되는 함수입니다.

def parse_snt_file(snt_file):
   
   if not olefile.isOleFile(snt_file):
      print("This is not an OLE file")
      return None
   ole = olefile.OleFileIO(snt_file)
   note = {}
   
   for stream in ole.listdir():
      if stream[0].count("-") == 3:
         if stream[0] not in note:
            note[stream[0]] = {"created": ole.getctime(stream[0]),"modified": ole.getmtime(stream[0])}
         content = None
         if stream[1] == '0':
            content = ole.openstream(stream).read()
         elif stream[1] == '3':
            content = ole.openstream(stream).read().decode("utf-16")
         if content:
            note[stream[0]][stream[1]] = content
	return note

이제 다음을 정의하여 RTF 파일을 만듭니다. write_note_rtf() 다음과 같이 기능

def write_note_rtf(note_data, report_folder):
   if not os.path.exists(report_folder):
      os.makedirs(report_folder)
   
   for note_id, stream_data in note_data.items():
      fname = os.path.join(report_folder, note_id + ".rtf")
      with open(fname, 'w') as open_file:
         open_file.write(stream_data['0'])

이제 중첩 된 사전을 CSV 스프레드 시트에 더 적합한 단순 사전 목록으로 변환합니다. 정의하여 수행됩니다.prep_note_report()함수. 마지막으로write_csv() 함수.

def prep_note_report(note_data, report_cols, note_file):
   report_details = []
   
   for note_id, stream_data in note_data.items():
      report_details.append({
         "note_id": note_id,
         "created": stream_data['created'],
         "modified": stream_data['modified'],
         "note_text": stream_data['3'].strip("\x00"),
         "note_file": note_file
      })
   return report_details
def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

위의 스크립트를 실행 한 후 스티커 메모 파일에서 메타 데이터를 가져옵니다.

레지스트리 파일

Windows 레지스트리 파일에는 포렌식 분석가를위한 보물 창고와 같은 중요한 세부 정보가 많이 포함되어 있습니다. 운영 체제 구성, 사용자 활동, 소프트웨어 설치 등과 관련된 세부 정보를 포함하는 계층 적 데이터베이스입니다. 다음 Python 스크립트에서는 다음에서 공통 기준 정보에 액세스합니다.SYSTEMSOFTWARE 두드러기.

이 Python 스크립트의 경우 타사 모듈을 설치해야합니다. pytsk3, pyewfregistry. 우리는 사용할 수 있습니다pip 설치합니다.

Windows 레지스트리에서 정보를 추출하려면 아래 단계를 따를 수 있습니다.

  • 먼저, 이름과 경로로 처리 할 레지스트리 하이브를 찾습니다.

  • 그런 다음 StringIO 및 레지스트리 모듈을 사용하여 이러한 파일을 엽니 다.

  • 마지막으로 모든 하이브를 처리하고 해석을 위해 구문 분석 된 값을 콘솔에 인쇄해야합니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser

import datetime
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기에서는 두 개의 인수를받습니다. 첫 번째는 증거 파일의 경로이고 두 번째는 아래에 표시된 증거 파일의 유형입니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Evidence from Windows Registry')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",
   choices = ('ewf', 'raw'))
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE)

이제 우리는 main() 검색 기능 SYSTEMSOFTWARE 내 두드러기 /Windows/System32/config 다음과 같이 폴더-

def main(evidence, image_type):
   tsk_util = TSKUtil(evidence, image_type)
   tsk_system_hive = tsk_util.recurse_files('system', '/Windows/system32/config', 'equals')
   tsk_software_hive = tsk_util.recurse_files('software', '/Windows/system32/config', 'equals')
   system_hive = open_file_as_reg(tsk_system_hive[0][2])
   software_hive = open_file_as_reg(tsk_software_hive[0][2])
   process_system_hive(system_hive)
   process_software_hive(software_hive)

이제 레지스트리 파일을 여는 기능을 정의하십시오. 이를 위해 파일 크기를 수집해야합니다.pytsk 다음과 같이 메타 데이터-

def open_file_as_reg(reg_file):
   file_size = reg_file.info.meta.size
   file_content = reg_file.read_random(0, file_size)
   file_like_obj = StringIO.StringIO(file_content)
   return Registry.Registry(file_like_obj)

이제 다음과 같은 방법으로 처리 할 수 ​​있습니다. SYSTEM> 하이브-

def process_system_hive(hive):
   root = hive.root()
   current_control_set = root.find_key("Select").value("Current").value()
   control_set = root.find_key("ControlSet{:03d}".format(current_control_set))
   raw_shutdown_time = struct.unpack(
      '<Q', control_set.find_key("Control").find_key("Windows").value("ShutdownTime").value())
   
   shutdown_time = parse_windows_filetime(raw_shutdown_time[0])
   print("Last Shutdown Time: {}".format(shutdown_time))
   
   time_zone = control_set.find_key("Control").find_key("TimeZoneInformation")
      .value("TimeZoneKeyName").value()
   
   print("Machine Time Zone: {}".format(time_zone))
   computer_name = control_set.find_key("Control").find_key("ComputerName").find_key("ComputerName")
      .value("ComputerName").value()
   
   print("Machine Name: {}".format(computer_name))
   last_access = control_set.find_key("Control").find_key("FileSystem")
      .value("NtfsDisableLastAccessUpdate").value()
   last_access = "Disabled" if last_access == 1 else "enabled"
   print("Last Access Updates: {}".format(last_access))

이제 다음과 같이 형식화 된 날짜와 시간으로 해석 된 정수에 대한 함수를 정의해야합니다.

def parse_windows_filetime(date_value):
   microseconds = float(date_value) / 10
   ts = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds = microseconds)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

def parse_unix_epoch(date_value):
   ts = datetime.datetime.fromtimestamp(date_value)
   return ts.strftime('%Y-%m-%d %H:%M:%S.%f')

이제 다음 방법의 도움으로 우리는 SOFTWARE 하이브-

def process_software_hive(hive):
   root = hive.root()
   nt_curr_ver = root.find_key("Microsoft").find_key("Windows NT")
      .find_key("CurrentVersion")
   
   print("Product name: {}".format(nt_curr_ver.value("ProductName").value()))
   print("CSD Version: {}".format(nt_curr_ver.value("CSDVersion").value()))
   print("Current Build: {}".format(nt_curr_ver.value("CurrentBuild").value()))
   print("Registered Owner: {}".format(nt_curr_ver.value("RegisteredOwner").value()))
   print("Registered Org: 
      {}".format(nt_curr_ver.value("RegisteredOrganization").value()))
   
   raw_install_date = nt_curr_ver.value("InstallDate").value()
   install_date = parse_unix_epoch(raw_install_date)
   print("Installation Date: {}".format(install_date))

위의 스크립트를 실행 한 후 Windows 레지스트리 파일에 저장된 메타 데이터를 가져옵니다.

이 장에서는 Windows의 몇 가지 중요한 아티팩트와 Python을 사용한 추출 방법에 대해 설명합니다.

사용자 활동

Windows 데 NTUSER.DAT다양한 사용자 활동을 저장하기위한 파일. 모든 사용자 프로필에는 다음과 같은 하이브가 있습니다.NTUSER.DAT, 특정 사용자와 관련된 정보 및 구성을 저장합니다. 따라서 법의학 분석가의 조사 목적에 매우 유용합니다.

다음 Python 스크립트는 다음의 일부 키를 구문 분석합니다. NTUSER.DAT시스템에서 사용자의 행동을 탐색합니다. 계속 진행하기 전에 Python 스크립트의 경우 타사 모듈을 설치해야합니다.Registry, pytsk3, pyewf 및 Jinja2. pip를 사용하여 설치할 수 있습니다.

다음 단계에 따라 정보를 추출 할 수 있습니다. NTUSER.DAT 파일-

  • 먼저 모두 검색 NTUSER.DAT 시스템의 파일.

  • 그런 다음 WordWheelQuery, TypePath and RunMRU 각 키 NTUSER.DAT 파일.

  • 마지막으로 이미 처리 된 이러한 아티팩트를 다음을 사용하여 HTML 보고서에 작성합니다. Jinja2 fmodule.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

우선, 다음 Python 모듈을 가져와야합니다.

from __future__ import print_function
from argparse import ArgumentParser

import os
import StringIO
import struct

from utility.pytskutil import TSKUtil
from Registry import Registry
import jinja2

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기에서 세 가지 인수를 허용합니다. 첫 번째는 증거 파일의 경로, 두 번째는 증거 파일의 유형, 세 번째는 아래에 표시된 HTML 보고서의 원하는 출력 경로입니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Information from user activities')
   parser.add_argument('EVIDENCE_FILE',help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE',help = "Evidence file format",choices = ('ewf', 'raw'))
   parser.add_argument('REPORT',help = "Path to report file")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.REPORT)

이제 정의하겠습니다. main() 모두 검색 기능 NTUSER.DAT 다음과 같이 파일-

def main(evidence, image_type, report):
   tsk_util = TSKUtil(evidence, image_type)
   tsk_ntuser_hives = tsk_util.recurse_files('ntuser.dat','/Users', 'equals')
   
   nt_rec = {
      'wordwheel': {'data': [], 'title': 'WordWheel Query'},
      'typed_path': {'data': [], 'title': 'Typed Paths'},
      'run_mru': {'data': [], 'title': 'Run MRU'}
   }

이제 우리는 키를 찾으려고 노력할 것입니다. NTUSER.DAT 파일을 찾으면 아래와 같이 사용자 처리 기능을 정의하십시오.

for ntuser in tsk_ntuser_hives:
   uname = ntuser[1].split("/")

open_ntuser = open_file_as_reg(ntuser[2])
try:
   explorer_key = open_ntuser.root().find_key("Software").find_key("Microsoft")
      .find_key("Windows").find_key("CurrentVersion").find_key("Explorer")
   except Registry.RegistryKeyNotFoundException:
      continue
   nt_rec['wordwheel']['data'] += parse_wordwheel(explorer_key, uname)
   nt_rec['typed_path']['data'] += parse_typed_paths(explorer_key, uname)
   nt_rec['run_mru']['data'] += parse_run_mru(explorer_key, uname)
   nt_rec['wordwheel']['headers'] = \ nt_rec['wordwheel']['data'][0].keys()
   nt_rec['typed_path']['headers'] = \ nt_rec['typed_path']['data'][0].keys()
   nt_rec['run_mru']['headers'] = \ nt_rec['run_mru']['data'][0].keys()

이제 사전 객체와 해당 경로를 write_html() 다음과 같이 방법-

write_html(report, nt_rec)

이제 방법을 정의하십시오. pytsk 파일 핸들을 사용하여 레지스트리 클래스로 읽어들입니다. StringIO 수업.

def open_file_as_reg(reg_file):
   file_size = reg_file.info.meta.size
   file_content = reg_file.read_random(0, file_size)
   file_like_obj = StringIO.StringIO(file_content)
   return Registry.Registry(file_like_obj)

이제 우리는 파싱하고 처리 할 함수를 정의 할 것입니다. WordWheelQuery 키에서 NTUSER.DAT 다음과 같이 파일-

def parse_wordwheel(explorer_key, username):
   try:
      wwq = explorer_key.find_key("WordWheelQuery")
   except Registry.RegistryKeyNotFoundException:
      return []
   mru_list = wwq.value("MRUListEx").value()
   mru_order = []
   
   for i in xrange(0, len(mru_list), 2):
      order_val = struct.unpack('h', mru_list[i:i + 2])[0]
   if order_val in mru_order and order_val in (0, -1):
      break
   else:
      mru_order.append(order_val)
   search_list = []
   
   for count, val in enumerate(mru_order):
      ts = "N/A"
      if count == 0:
         ts = wwq.timestamp()
      search_list.append({
         'timestamp': ts,
         'username': username,
         'order': count,
         'value_name': str(val),
         'search': wwq.value(str(val)).value().decode("UTF-16").strip("\x00")
})
   return search_list

이제 우리는 파싱하고 처리 할 함수를 정의 할 것입니다. TypedPaths 키에서 NTUSER.DAT 다음과 같이 파일-

def parse_typed_paths(explorer_key, username):
   try:
      typed_paths = explorer_key.find_key("TypedPaths")
   except Registry.RegistryKeyNotFoundException:
      return []
   typed_path_details = []
   
   for val in typed_paths.values():
      typed_path_details.append({
         "username": username,
         "value_name": val.name(),
         "path": val.value()
      })
   return typed_path_details

이제 우리는 파싱하고 처리 할 함수를 정의 할 것입니다. RunMRU 키에서 NTUSER.DAT 다음과 같이 파일-

def parse_run_mru(explorer_key, username):
   try:
      run_mru = explorer_key.find_key("RunMRU")
   except Registry.RegistryKeyNotFoundException:
      return []
   
   if len(run_mru.values()) == 0:
      return []
   mru_list = run_mru.value("MRUList").value()
   mru_order = []
   
   for i in mru_list:
      mru_order.append(i)
   mru_details = []
   
   for count, val in enumerate(mru_order):
      ts = "N/A"
      if count == 0:
         ts = run_mru.timestamp()
      mru_details.append({
         "username": username,
         "timestamp": ts,
         "order": count,
         "value_name": val,
         "run_statement": run_mru.value(val).value()
      })
   return mru_details

이제 다음 함수가 HTML 보고서 생성을 처리합니다.

def write_html(outfile, data_dict):
   cwd = os.path.dirname(os.path.abspath(__file__))
   env = jinja2.Environment(loader=jinja2.FileSystemLoader(cwd))
   template = env.get_template("user_activity.html")
   rendering = template.render(nt_data=data_dict)
   
   with open(outfile, 'w') as open_outfile:
      open_outfile.write(rendering)

드디어 보고서 용 HTML 문서를 작성할 수 있습니다. 위의 스크립트를 실행 한 후 HTML 문서 형식의 NTUSER.DAT 파일에서 정보를 가져옵니다.

LINK 파일

바로 가기 파일은 사용자 또는 운영 체제가 자주 사용하거나 두 번 클릭하거나 연결된 저장소와 같은 시스템 드라이브에서 액세스하는 파일에 대한 바로 가기 파일을 만들 때 생성됩니다. 이러한 종류의 바로 가기 파일을 링크 파일이라고합니다. 이러한 링크 파일에 액세스하여 조사자는 이러한 파일에 액세스 한 시간 및 위치와 같은 창의 활동을 찾을 수 있습니다.

이러한 Windows LINK 파일에서 정보를 가져 오는 데 사용할 수있는 Python 스크립트에 대해 설명하겠습니다.

Python 스크립트의 경우 타사 모듈을 설치합니다. pylnk, pytsk3, pyewf. 다음 단계에 따라 정보를 추출 할 수 있습니다.lnk 파일

  • 먼저 lnk 시스템 내의 파일.

  • 그런 다음 해당 파일을 반복하여 정보를 추출하십시오.

  • 이제 마침내이 정보를 CSV 보고서에 추가해야합니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser

import csv
import StringIO

from utility.pytskutil import TSKUtil
import pylnk

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기에서 세 가지 인수를받습니다. 첫 번째는 증거 파일의 경로, 두 번째는 증거 파일의 유형, 세 번째는 아래에 표시된 CSV 보고서의 원하는 출력 경로입니다.

if __name__ == '__main__':
   parser = argparse.ArgumentParser('Parsing LNK files')
   parser.add_argument('EVIDENCE_FILE', help = "Path to evidence file")
   parser.add_argument('IMAGE_TYPE', help = "Evidence file format",choices = ('ewf', 'raw'))
   parser.add_argument('CSV_REPORT', help = "Path to CSV report")
   args = parser.parse_args()
   main(args.EVIDENCE_FILE, args.IMAGE_TYPE, args.CSV_REPORT)

이제 다음 개체를 만들어 증거 파일을 해석합니다. TSKUtil 파일 시스템을 반복하여 다음으로 끝나는 파일을 찾습니다. lnk. 정의하여 수행 할 수 있습니다.main() 다음과 같이 기능-

def main(evidence, image_type, report):
   tsk_util = TSKUtil(evidence, image_type)
   lnk_files = tsk_util.recurse_files("lnk", path="/", logic="endswith")
   
   if lnk_files is None:
      print("No lnk files found")
      exit(0)
   columns = [
      'command_line_arguments', 'description', 'drive_serial_number',
      'drive_type', 'file_access_time', 'file_attribute_flags',
      'file_creation_time', 'file_modification_time', 'file_size',
      'environmental_variables_location', 'volume_label',
      'machine_identifier', 'local_path', 'network_path',
      'relative_path', 'working_directory'
   ]

이제 다음 코드의 도움을 받아 lnk 다음과 같이 함수를 생성하여 파일-

parsed_lnks = []

for entry in lnk_files:
   lnk = open_file_as_lnk(entry[2])
   lnk_data = {'lnk_path': entry[1], 'lnk_name': entry[0]}
   
   for col in columns:
      lnk_data[col] = getattr(lnk, col, "N/A")
   lnk.close()
   parsed_lnks.append(lnk_data)
write_csv(report, columns + ['lnk_path', 'lnk_name'], parsed_lnks)

이제 두 개의 함수를 정의해야합니다. 하나는 pytsk 파일 개체 및 기타는 아래와 같이 CSV 보고서를 작성하는 데 사용됩니다-

def open_file_as_lnk(lnk_file):
   file_size = lnk_file.info.meta.size
   file_content = lnk_file.read_random(0, file_size)
   file_like_obj = StringIO.StringIO(file_content)
   lnk = pylnk.file()
   lnk.open_file_object(file_like_obj)
   return lnk
def write_csv(outfile, fieldnames, data):
   with open(outfile, 'wb') as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

위의 스크립트를 실행 한 후 발견 된 lnk CSV 보고서의 파일-

파일 프리 페치

응용 프로그램이 특정 위치에서 처음으로 실행될 때마다 Windows는 prefetch files. 이는 애플리케이션 시작 프로세스의 속도를 높이는 데 사용됩니다. 이 파일의 확장자는.PF 그리고 이것들은 ”\Root\Windows\Prefetch” 폴더.

디지털 포렌식 전문가는 사용자의 세부 정보와 함께 지정된 위치에서 프로그램 실행 증거를 공개 할 수 있습니다. 프리 페치 파일은 프로그램이 삭제되거나 제거 된 후에도 항목이 남아 있기 때문에 검사자에게 유용한 아티팩트입니다.

아래와 같이 Windows 프리 페치 파일에서 정보를 가져 오는 Python 스크립트에 대해 설명하겠습니다.

Python 스크립트의 경우 타사 모듈을 설치합니다. pylnk, pytsk3unicodecsv. 이전 장에서 논의한 Python 스크립트에서 이미 이러한 라이브러리로 작업했음을 상기하십시오.

정보를 추출하려면 아래 단계를 따라야합니다. prefetch 파일-

  • 먼저 .pf 확장 파일 또는 프리 페치 파일.

  • 이제 서명 확인을 수행하여 오탐을 제거하십시오.

  • 다음으로 Windows 프리 페치 파일 형식을 구문 분석합니다. 이것은 Windows 버전에 따라 다릅니다. 예를 들어 Windows XP의 경우 17, Windows Vista 및 Windows 7의 경우 23, Windows 8.1의 경우 26, Windows 10의 경우 30입니다.

  • 마지막으로 파싱 된 결과를 CSV 파일로 작성합니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse
from datetime import datetime, timedelta

import os
import pytsk3
import pyewf
import struct
import sys
import unicodecsv as csv
from utility.pytskutil import TSKUtil

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 첫 번째는 증거 파일의 경로이고 두 번째는 증거 파일의 유형입니다. 또한 프리 페치 파일을 스캔 할 경로를 지정하기위한 선택적 인수를 허용합니다.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Parsing Prefetch files')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument("OUTPUT_CSV", help = "Path to write output csv")
   parser.add_argument("-d", help = "Prefetch directory to scan",default = "/WINDOWS/PREFETCH")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and \
      os.path.isfile(args.EVIDENCE_FILE):
   main(args.EVIDENCE_FILE, args.TYPE, args.OUTPUT_CSV, args.d)
else:
   print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
   sys.exit(1)

이제 다음 개체를 만들어 증거 파일을 해석합니다. TSKUtil 파일 시스템을 반복하여 다음으로 끝나는 파일을 찾습니다. .pf. 정의하여 수행 할 수 있습니다.main() 다음과 같이 기능-

def main(evidence, image_type, output_csv, path):
   tsk_util = TSKUtil(evidence, image_type)
   prefetch_dir = tsk_util.query_directory(path)
   prefetch_files = None
   
   if prefetch_dir is not None:
      prefetch_files = tsk_util.recurse_files(".pf", path=path, logic="endswith")
   
   if prefetch_files is None:
      print("[-] No .pf files found")
      sys.exit(2)
   print("[+] Identified {} potential prefetch files".format(len(prefetch_files)))
   prefetch_data = []
   
   for hit in prefetch_files:
      prefetch_file = hit[2]
      pf_version = check_signature(prefetch_file)

이제 아래와 같이 서명의 유효성을 검사하는 방법을 정의합니다.

def check_signature(prefetch_file):
   version, signature = struct.unpack("^<2i", prefetch_file.read_random(0, 8))
   
   if signature == 1094927187:
      return version
   else:
      return None
   
   if pf_version is None:
      continue
   pf_name = hit[0]
   
   if pf_version == 17:
      parsed_data = parse_pf_17(prefetch_file, pf_name)
      parsed_data.append(os.path.join(path, hit[1].lstrip("//")))
      prefetch_data.append(parsed_data)

이제 Windows 프리 페치 파일 처리를 시작하십시오. 여기에서는 Windows XP 프리 페치 파일의 예를 살펴 보겠습니다.

def parse_pf_17(prefetch_file, pf_name):
   create = convert_unix(prefetch_file.info.meta.crtime)
   modify = convert_unix(prefetch_file.info.meta.mtime)
def convert_unix(ts):
   if int(ts) == 0:
      return ""
   return datetime.utcfromtimestamp(ts)
def convert_filetime(ts):
   if int(ts) == 0:
      return ""
   return datetime(1601, 1, 1) + timedelta(microseconds=ts / 10)

이제 다음과 같이 struct를 사용하여 프리 페치 된 파일에 포함 된 데이터를 추출합니다.

pf_size, name, vol_info, vol_entries, vol_size, filetime, \
   count = struct.unpack("<i60s32x3iq16xi",prefetch_file.read_random(12, 136))
name = name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]

vol_name_offset, vol_name_length, vol_create, \
   vol_serial = struct.unpack("<2iqi",prefetch_file.read_random(vol_info, 20))
   vol_serial = hex(vol_serial).lstrip("0x")
   vol_serial = vol_serial[:4] + "-" + vol_serial[4:]
   vol_name = struct.unpack(
      "<{}s".format(2 * vol_name_length),
      prefetch_file.read_random(vol_info + vol_name_offset,vol_name_length * 2))[0]

vol_name = vol_name.decode("utf-16", "ignore").strip("/x00").split("/x00")[0]
return [
   pf_name, name, pf_size, create,
   modify, convert_filetime(filetime), count, vol_name,
   convert_filetime(vol_create), vol_serial ]

Windows XP 용 프리 페치 버전을 제공했지만 다른 Windows 용 프리 페치 버전이 발생하면 어떻게됩니까? 그런 다음 다음과 같이 오류 메시지를 표시해야합니다.

elif pf_version == 23:
   print("[-] Windows Vista / 7 PF file {} -- unsupported".format(pf_name))
   continue
elif pf_version == 26:
   print("[-] Windows 8 PF file {} -- unsupported".format(pf_name))
   continue
elif pf_version == 30:
   print("[-] Windows 10 PF file {} -- unsupported".format(pf_name))
continue

else:
   print("[-] Signature mismatch - Name: {}\nPath: {}".format(hit[0], hit[1]))
continue
write_output(prefetch_data, output_csv)

이제 결과를 CSV 보고서에 쓰는 방법을 다음과 같이 정의하십시오.

def write_output(data, output_csv):
   print("[+] Writing csv report")
   with open(output_csv, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow([
         "File Name", "Prefetch Name", "File Size (bytes)",
         "File Create Date (UTC)", "File Modify Date (UTC)",
         "Prefetch Last Execution Date (UTC)",
         "Prefetch Execution Count", "Volume", "Volume Create Date",
         "Volume Serial", "File Path" ])
      writer.writerows(data)

위의 스크립트를 실행 한 후 Windows XP 버전의 프리 페치 파일에서 스프레드 시트로 정보를 가져옵니다.

이 장에서는 조사자가 Windows에서 포렌식 분석 중에 얻을 수있는 추가 아티팩트에 대해 설명합니다.

이벤트 로그

이름이 제안하는 Windows 이벤트 로그 파일은 사용자가 컴퓨터에 로그온 할 때, 프로그램에 오류가 발생할 때, 시스템 변경, RDP 액세스, 애플리케이션 특정 이벤트 등에 대한 중요한 이벤트를 저장하는 특수 파일입니다. 사이버 조사자는 항상 이벤트에 관심이 있습니다. 시스템 액세스에 대한 유용한 기록 정보를 많이 제공하므로 정보를 기록합니다. 다음 Python 스크립트에서 레거시 및 현재 Windows 이벤트 로그 형식을 모두 처리합니다.

Python 스크립트의 경우 타사 모듈을 설치해야합니다. pytsk3, pyewf, unicodecsv, pyevt and pyevt엑스. 아래 단계에 따라 이벤트 로그에서 정보를 추출 할 수 있습니다.

  • 먼저 입력 인수와 일치하는 모든 이벤트 로그를 검색합니다.

  • 그런 다음 파일 서명 확인을 수행하십시오.

  • 이제 적절한 라이브러리에서 찾은 각 이벤트 로그를 처리합니다.

  • 마지막으로 출력을 스프레드 시트에 씁니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse
import unicodecsv as csv
import os
import pytsk3
import pyewf
import pyevt
import pyevtx
import sys
from utility.pytskutil import TSKUtil

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서는 세 개의 인수를받습니다. 첫 번째는 증거 파일의 경로, 두 번째는 증거 파일의 유형, 세 번째는 처리 할 이벤트 로그의 이름입니다.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Information from Event Logs')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument(
      "LOG_NAME",help = "Event Log Name (SecEvent.Evt, SysEvent.Evt, ""etc.)")
   
   parser.add_argument(
      "-d", help = "Event log directory to scan",default = "/WINDOWS/SYSTEM32/WINEVT")
   
   parser.add_argument(
      "-f", help = "Enable fuzzy search for either evt or"" evtx extension", action = "store_true")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
      main(args.EVIDENCE_FILE, args.TYPE, args.LOG_NAME, args.d, args.f)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
   sys.exit(1)

이제 이벤트 로그와 상호 작용하여 사용자 제공 경로의 존재를 쿼리합니다. TSKUtil목적. 그것은 도움으로 할 수 있습니다main() 다음과 같이 방법-

def main(evidence, image_type, log, win_event, fuzzy):
   tsk_util = TSKUtil(evidence, image_type)
   event_dir = tsk_util.query_directory(win_event)
   
   if event_dir is not None:
      if fuzzy is True:
         event_log = tsk_util.recurse_files(log, path=win_event)
   else:
      event_log = tsk_util.recurse_files(log, path=win_event, logic="equal")
   
   if event_log is not None:
      event_data = []
      for hit in event_log:
         event_file = hit[2]
         temp_evt = write_file(event_file)

이제 서명 확인을 수행 한 다음 전체 내용을 현재 디렉토리에 기록하는 방법을 정의해야합니다.

def write_file(event_file):
   with open(event_file.info.name.name, "w") as outfile:
      outfile.write(event_file.read_random(0, event_file.info.meta.size))
   return event_file.info.name.name
      if pyevt.check_file_signature(temp_evt):
         evt_log = pyevt.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evt_log.number_of_records, temp_evt))
         
         for i, record in enumerate(evt_log.records):
            strings = ""
            for s in record.strings:
               if s is not None:
                  strings += s + "\n"
            event_data.append([
               i, hit[0], record.computer_name,
               record.user_security_identifier,
               record.creation_time, record.written_time,
               record.event_category, record.source_name,
               record.event_identifier, record.event_type,
               strings, "",
               os.path.join(win_event, hit[1].lstrip("//"))
            ])
      elif pyevtx.check_file_signature(temp_evt):
         evtx_log = pyevtx.open(temp_evt)
         print("[+] Identified {} records in {}".format(
            evtx_log.number_of_records, temp_evt))
         for i, record in enumerate(evtx_log.records):
            strings = ""
            for s in record.strings:
			   if s is not None:
               strings += s + "\n"
         event_data.append([
            i, hit[0], record.computer_name,
            record.user_security_identifier, "",
            record.written_time, record.event_level,
            record.source_name, record.event_identifier,
            "", strings, record.xml_string,
            os.path.join(win_event, hit[1].lstrip("//"))
      ])
      else:
         print("[-] {} not a valid event log. Removing temp" file...".format(temp_evt))
         os.remove(temp_evt)
      continue
      write_output(event_data)
   else:
      print("[-] {} Event log not found in {} directory".format(log, win_event))
      sys.exit(3)
else:
   print("[-] Win XP Event Log Directory {} not found".format(win_event))
   sys.exit(2

마지막으로 다음과 같이 스프레드 시트에 출력을 쓰는 방법을 정의합니다.

def write_output(data):
   output_name = "parsed_event_logs.csv"
   print("[+] Writing {} to current working directory: {}".format(
      output_name, os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow([
         "Index", "File name", "Computer Name", "SID",
         "Event Create Date", "Event Written Date",
         "Event Category/Level", "Event Source", "Event ID",
         "Event Type", "Data", "XML Data", "File Path"
      ])
      writer.writerows(data)

위의 스크립트를 성공적으로 실행하면 스프레드 시트에서 이벤트 로그 정보를 얻을 수 있습니다.

인터넷 역사

인터넷 기록은 법의학 분석가에게 매우 유용합니다. 대부분의 사이버 범죄는 인터넷을 통해서만 발생합니다. Windows 포렌식에 대해 논의하면서 Internet Explorer에서 인터넷 기록을 추출하는 방법을 살펴 보겠습니다. Internet Explorer는 기본적으로 Windows와 함께 제공됩니다.

Internet Explorer에서는 인터넷 기록이 index.dat파일. 정보를 추출 할 Python 스크립트를 살펴 보겠습니다.index.dat 파일.

아래 단계에 따라 정보를 추출 할 수 있습니다. index.dat 파일-

  • 먼저 index.dat 시스템 내의 파일.

  • 그런 다음 해당 파일을 반복하여 정보를 추출하십시오.

  • 이제이 모든 정보를 CSV 보고서에 씁니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse

from datetime import datetime, timedelta
import os
import pytsk3
import pyewf
import pymsiecf
import sys
import unicodecsv as csv

from utility.pytskutil import TSKUtil

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를 허용합니다. 첫 번째는 증거 파일의 경로이고 두 번째는 증거 파일의 유형입니다.

if __name__ == "__main__":
parser = argparse.ArgumentParser('getting information from internet history')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("TYPE", help = "Type of Evidence",choices = ("raw", "ewf"))
   parser.add_argument("-d", help = "Index.dat directory to scan",default = "/USERS")
   args = parser.parse_args()
   
   if os.path.exists(args.EVIDENCE_FILE) and os.path.isfile(args.EVIDENCE_FILE):
      main(args.EVIDENCE_FILE, args.TYPE, args.d)
   else:
      print("[-] Supplied input file {} does not exist or is not a ""file".format(args.EVIDENCE_FILE))
      sys.exit(1)

이제 다음 개체를 만들어 증거 파일을 해석합니다. TSKUtilindex.dat 파일을 찾기 위해 파일 시스템을 반복합니다. 정의하여 수행 할 수 있습니다.main() 다음과 같이 기능-

def main(evidence, image_type, path):
   tsk_util = TSKUtil(evidence, image_type)
   index_dir = tsk_util.query_directory(path)
   
   if index_dir is not None:
      index_files = tsk_util.recurse_files("index.dat", path = path,logic = "equal")
      
      if index_files is not None:
         print("[+] Identified {} potential index.dat files".format(len(index_files)))
         index_data = []
         
         for hit in index_files:
            index_file = hit[2]
            temp_index = write_file(index_file)

이제 index.dat 파일의 정보를 현재 작업 디렉토리에 복사하고 나중에 타사 모듈에서 처리 할 수있는 함수를 정의합니다.

def write_file(index_file):
   with open(index_file.info.name.name, "w") as outfile:
   outfile.write(index_file.read_random(0, index_file.info.meta.size))
return index_file.info.name.name

이제 다음 코드를 사용하여 내장 함수의 도움으로 서명 유효성 검사를 수행하십시오. check_file_signature()

if pymsiecf.check_file_signature(temp_index):
   index_dat = pymsiecf.open(temp_index)
   print("[+] Identified {} records in {}".format(
   index_dat.number_of_items, temp_index))

   for i, record in enumerate(index_dat.items):
   try:
      data = record.data
   if data is not None:
      data = data.rstrip("\x00")
   except AttributeError:
   
   if isinstance(record, pymsiecf.redirected):
      index_data.append([
         i, temp_index, "", "", "", "", "",record.location, "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   
   elif isinstance(record, pymsiecf.leak):
      index_data.append([
         i, temp_index, record.filename, "","", "", "", "", "", "", record.offset,os.path.join(path, hit[1].lstrip("//"))])
   continue
   
   index_data.append([
      i, temp_index, record.filename,
      record.type, record.primary_time,
      record.secondary_time,
      record.last_checked_time, record.location,
      record.number_of_hits, data, record.offset,
      os.path.join(path, hit[1].lstrip("//"))
   ])
   else:
      print("[-] {} not a valid index.dat file. Removing "
      "temp file..".format(temp_index))
      os.remove("index.dat")
      continue
      os.remove("index.dat")
      write_output(index_data)
   else:
      print("[-] Index.dat files not found in {} directory".format(path))
   sys.exit(3)
   else:
      print("[-] Directory {} not found".format(win_event))
   sys.exit(2)

이제 아래와 같이 CSV 파일로 출력을 인쇄하는 방법을 정의하십시오.

def write_output(data):
   output_name = "Internet_Indexdat_Summary_Report.csv"
   print("[+] Writing {} with {} parsed index.dat files to current "
   "working directory: {}".format(output_name, len(data),os.getcwd()))
   
   with open(output_name, "wb") as outfile:
      writer = csv.writer(outfile)
      writer.writerow(["Index", "File Name", "Record Name",
      "Record Type", "Primary Date", "Secondary Date",
      "Last Checked Date", "Location", "No. of Hits",
      "Record Data", "Record Offset", "File Path"])
      writer.writerows(data)

위의 스크립트를 실행하면 CSV 파일의 index.dat 파일에서 정보를 가져옵니다.

볼륨 섀도 복사본

섀도 복사본은 컴퓨터 파일의 백업 복사본 또는 스냅 샷을 수동 또는 자동으로 만들기 위해 Windows에 포함 된 기술입니다. 볼륨 스냅 샷 서비스 또는 VSS (볼륨 섀도우 서비스)라고도합니다.

이러한 VSS 파일의 도움으로 법의학 전문가는 시간이 지남에 따라 시스템이 어떻게 변경되었는지와 컴퓨터에 존재하는 파일에 대한 몇 가지 과거 정보를 얻을 수 있습니다. 섀도 복사본 기술을 사용하려면 섀도 복사본을 만들고 저장하려면 파일 시스템이 NTFS 여야합니다.

이 섹션에서는 포렌식 이미지에있는 모든 볼륨의 섀도 복사본에 액세스하는 데 도움이되는 Python 스크립트를 살펴 보겠습니다.

Python 스크립트의 경우 타사 모듈을 설치해야합니다. pytsk3, pyewf, unicodecsv, pyvshadowvss. 아래 단계에 따라 VSS 파일에서 정보를 추출 할 수 있습니다.

  • 먼저 원시 이미지의 볼륨에 액세스하고 모든 NTFS 파티션을 식별합니다.

  • 그런 다음 섀도 복사본을 반복하여 정보를 추출합니다.

  • 이제 마지막으로 스냅 샷 내에 데이터 목록을 파일로 만들어야합니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 라이브러리를 가져옵니다.

from __future__ import print_function
import argparse
from datetime import datetime, timedelta

import os
import pytsk3
import pyewf
import pyvshadow
import sys
import unicodecsv as csv

from utility import vss
from utility.pytskutil import TSKUtil
from utility import pytskutil

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 첫 번째는 증거 파일의 경로이고 두 번째는 출력 파일입니다.

if __name__ == "__main__":
   parser = argparse.ArgumentParser('Parsing Shadow Copies')
   parser.add_argument("EVIDENCE_FILE", help = "Evidence file path")
   parser.add_argument("OUTPUT_CSV", help = "Output CSV with VSS file listing")
   args = parser.parse_args()

이제 입력 파일 경로의 존재를 확인하고 디렉토리를 출력 파일과 분리합니다.

directory = os.path.dirname(args.OUTPUT_CSV)
if not os.path.exists(directory) and directory != "":
   os.makedirs(directory)
if os.path.exists(args.EVIDENCE_FILE) and \ os.path.isfile(args.EVIDENCE_FILE):
   main(args.EVIDENCE_FILE, args.OUTPUT_CSV)
else:
   print("[-] Supplied input file {} does not exist or is not a "
   "file".format(args.EVIDENCE_FILE))
   
   sys.exit(1)

이제 증거 파일의 볼륨과 상호 작용합니다. TSKUtil목적. 그것은 도움으로 할 수 있습니다main() 다음과 같이 방법-

def main(evidence, output):
   tsk_util = TSKUtil(evidence, "raw")
   img_vol = tsk_util.return_vol()

if img_vol is not None:
   for part in img_vol:
      if tsk_util.detect_ntfs(img_vol, part):
         print("Exploring NTFS Partition for VSS")
         explore_vss(evidence, part.start * img_vol.info.block_size,output)
      else:
         print("[-] Must be a physical preservation to be compatible ""with this script")
         sys.exit(2)

이제 다음과 같이 구문 분석 된 볼륨 섀도 파일을 탐색하는 방법을 정의합니다.

def explore_vss(evidence, part_offset, output):
   vss_volume = pyvshadow.volume()
   vss_handle = vss.VShadowVolume(evidence, part_offset)
   vss_count = vss.GetVssStoreCount(evidence, part_offset)
   
   if vss_count > 0:
      vss_volume.open_file_object(vss_handle)
      vss_data = []
      
      for x in range(vss_count):
         print("Gathering data for VSC {} of {}".format(x, vss_count))
         vss_store = vss_volume.get_store(x)
         image = vss.VShadowImgInfo(vss_store)
         vss_data.append(pytskutil.openVSSFS(image, x))
write_csv(vss_data, output)

마지막으로 스프레드 시트에 결과를 쓰는 방법을 다음과 같이 정의합니다.

def write_csv(data, output):
   if data == []:
      print("[-] No output results to write")
      sys.exit(3)
   print("[+] Writing output to {}".format(output))
   if os.path.exists(output):
      append = True
with open(output, "ab") as csvfile:
      csv_writer = csv.writer(csvfile)
      headers = ["VSS", "File", "File Ext", "File Type", "Create Date",
         "Modify Date", "Change Date", "Size", "File Path"]
      if not append:
         csv_writer.writerow(headers)
      for result_list in data:
         csv_writer.writerows(result_list)

이 Python 스크립트를 성공적으로 실행하면 VSS에있는 정보를 스프레드 시트로 가져옵니다.

지금까지 Python을 사용하여 Windows에서 아티팩트를 얻는 방법을 살펴 보았습니다. 이 장에서는 Python을 사용한 로그 기반 아티팩트 조사에 대해 알아 보겠습니다.

소개

로그 기반 인공물은 디지털 포렌식 전문가에게 매우 유용 할 수있는 정보의보고입니다. 정보 수집을위한 다양한 모니터링 소프트웨어가 있지만 유용한 정보를 파싱하는 주요 문제는 많은 데이터가 필요하다는 것입니다.

다양한 로그 기반 아티팩트 및 Python에서 조사

이 섹션에서는 다양한 로그 기반 아티팩트와 Python에서의 조사에 대해 설명하겠습니다.

타임 스탬프

타임 스탬프는 로그에서 활동의 데이터와 시간을 전달합니다. 모든 로그 파일의 중요한 요소 중 하나입니다. 이러한 데이터 및 시간 값은 다양한 형식으로 제공 될 수 있습니다.

아래 표시된 Python 스크립트는 원시 날짜-시간을 입력으로 사용하고 형식이 지정된 타임 스탬프를 출력으로 제공합니다.

이 스크립트의 경우 다음 단계를 따라야합니다.

  • 먼저 데이터 소스 및 데이터 유형과 함께 원시 데이터 값을 사용할 인수를 설정합니다.

  • 이제 다양한 날짜 형식의 데이터에 대한 공통 인터페이스를 제공하는 클래스를 제공하십시오.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 모듈을 가져옵니다.

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from datetime import datetime as dt
from datetime import timedelta

이제 평소와 같이 명령 줄 처리기에 대한 인수를 제공해야합니다. 여기에서 세 개의 인수를받습니다. 첫 번째는 처리 할 날짜 값, 두 번째는 해당 날짜 값의 소스, 세 번째는 해당 유형입니다.

if __name__ == '__main__':
   parser = ArgumentParser('Timestamp Log-based artifact')
   parser.add_argument("date_value", help="Raw date value to parse")
   parser.add_argument(
      "source", help = "Source format of date",choices = ParseDate.get_supported_formats())
   parser.add_argument(
      "type", help = "Data type of input value",choices = ('number', 'hex'), default = 'int')
   
   args = parser.parse_args()
   date_parser = ParseDate(args.date_value, args.source, args.type)
   date_parser.run()
   print(date_parser.timestamp)

이제 날짜 값, 날짜 소스 및 값 유형에 대한 인수를 허용하는 클래스를 정의해야합니다.

class ParseDate(object):
   def __init__(self, date_value, source, data_type):
      self.date_value = date_value
      self.source = source
      self.data_type = data_type
      self.timestamp = None

이제 main () 메서드처럼 컨트롤러처럼 작동하는 메서드를 정의합니다.

def run(self):
   if self.source == 'unix-epoch':
      self.parse_unix_epoch()
   elif self.source == 'unix-epoch-ms':
      self.parse_unix_epoch(True)
   elif self.source == 'windows-filetime':
      self.parse_windows_filetime()
@classmethod
def get_supported_formats(cls):
   return ['unix-epoch', 'unix-epoch-ms', 'windows-filetime']

이제 우리는 Unix epoch time과 FILETIME을 각각 처리 할 두 가지 방법을 정의해야합니다.

def parse_unix_epoch(self, milliseconds=False):
   if self.data_type == 'hex':
      conv_value = int(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   elif self.data_type == 'number':
      conv_value = float(self.date_value)
      if milliseconds:
         conv_value = conv_value / 1000.0
   else:
      print("Unsupported data type '{}' provided".format(self.data_type))
      sys.exit('1')
   ts = dt.fromtimestamp(conv_value)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')
def parse_windows_filetime(self):
   if self.data_type == 'hex':
      microseconds = int(self.date_value, 16) / 10.0
   elif self.data_type == 'number':
      microseconds = float(self.date_value) / 10
   else:
      print("Unsupported data type '{}'   provided".format(self.data_type))
      sys.exit('1')
   ts = dt(1601, 1, 1) + timedelta(microseconds=microseconds)
   self.timestamp = ts.strftime('%Y-%m-%d %H:%M:%S.%f')

위의 스크립트를 실행 한 후 타임 스탬프를 제공하면 읽기 쉬운 형식으로 변환 된 값을 얻을 수 있습니다.

웹 서버 로그

디지털 포렌식 전문가의 관점에서 웹 서버 로그는 사용자 및 지리적 위치에 대한 정보와 함께 유용한 사용자 통계를 얻을 수 있기 때문에 또 다른 중요한 아티팩트입니다. 다음은 정보를 쉽게 분석하기 위해 웹 서버 로그를 처리 한 후 스프레드 시트를 만드는 Python 스크립트입니다.

우선 다음 Python 모듈을 가져와야합니다.

from __future__ import print_function
from argparse import ArgumentParser, FileType

import re
import shlex
import logging
import sys
import csv

logger = logging.getLogger(__file__)

이제 로그에서 구문 분석 할 패턴을 정의해야합니다.

iis_log_format = [
   ("date", re.compile(r"\d{4}-\d{2}-\d{2}")),
   ("time", re.compile(r"\d\d:\d\d:\d\d")),
   ("s-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs-method", re.compile(
      r"(GET)|(POST)|(PUT)|(DELETE)|(OPTIONS)|(HEAD)|(CONNECT)")),
   ("cs-uri-stem", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("cs-uri-query", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("s-port", re.compile(r"\d*")),
   ("cs-username", re.compile(r"([A-Za-z0-1/\.-]*)")),
   ("c-ip", re.compile(
      r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}")),
   ("cs(User-Agent)", re.compile(r".*")),
   ("sc-status", re.compile(r"\d*")),
   ("sc-substatus", re.compile(r"\d*")),
   ("sc-win32-status", re.compile(r"\d*")),
   ("time-taken", re.compile(r"\d*"))]

이제 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를받습니다. 첫 번째는 처리 할 IIS 로그이고 두 번째는 원하는 CSV 파일 경로입니다.

if __name__ == '__main__':
   parser = ArgumentParser('Parsing Server Based Logs')
   parser.add_argument('iis_log', help = "Path to IIS Log",type = FileType('r'))
   parser.add_argument('csv_report', help = "Path to CSV report")
   parser.add_argument('-l', help = "Path to processing log",default=__name__ + '.log')
   args = parser.parse_args()
   logger.setLevel(logging.DEBUG)
   msg_fmt = logging.Formatter(
      "%(asctime)-15s %(funcName)-10s ""%(levelname)-8s %(message)s")
   
   strhndl = logging.StreamHandler(sys.stdout)
   strhndl.setFormatter(fmt = msg_fmt)
   fhndl = logging.FileHandler(args.log, mode = 'a')
   fhndl.setFormatter(fmt = msg_fmt)
   
   logger.addHandler(strhndl)
   logger.addHandler(fhndl)
   logger.info("Starting IIS Parsing ")
   logger.debug("Supplied arguments: {}".format(", ".join(sys.argv[1:])))
   logger.debug("System " + sys.platform)
   logger.debug("Version " + sys.version)
   main(args.iis_log, args.csv_report, logger)
   iologger.info("IIS Parsing Complete")

이제 대량 로그 정보에 대한 스크립트를 처리 할 main () 메서드를 정의해야합니다.

def main(iis_log, report_file, logger):
   parsed_logs = []

for raw_line in iis_log:
   line = raw_line.strip()
   log_entry = {}

if line.startswith("#") or len(line) == 0:
   continue

if '\"' in line:
   line_iter = shlex.shlex(line_iter)
else:
   line_iter = line.split(" ")
   for count, split_entry in enumerate(line_iter):
      col_name, col_pattern = iis_log_format[count]

      if col_pattern.match(split_entry):
         log_entry[col_name] = split_entry
else:
   logger.error("Unknown column pattern discovered. "
      "Line preserved in full below")
      logger.error("Unparsed Line: {}".format(line))
      parsed_logs.append(log_entry)
      
      logger.info("Parsed {} lines".format(len(parsed_logs)))
      cols = [x[0] for x in iis_log_format]
      
      logger.info("Creating report file: {}".format(report_file))
      write_csv(report_file, cols, parsed_logs)
      logger.info("Report created")

마지막으로 스프레드 시트에 출력을 기록하는 방법을 정의해야합니다.

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'w', newline="") as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

위의 스크립트를 실행 한 후 웹 서버 기반 로그를 스프레드 시트로 가져옵니다.

YARA를 사용하여 중요한 파일 스캔

YARA (Yet Another Recursive Algorithm)는 맬웨어 식별 및 사고 대응을 위해 설계된 패턴 일치 유틸리티입니다. 파일 스캔에 YARA를 사용합니다. 다음 Python 스크립트에서는 YARA를 사용합니다.

다음 명령을 사용하여 YARA를 설치할 수 있습니다.

pip install YARA

YARA 규칙을 사용하여 파일을 스캔하려면 아래 단계를 따를 수 있습니다.

  • 먼저 YARA 규칙을 설정하고 컴파일합니다.

  • 그런 다음 단일 파일을 스캔 한 다음 디렉터리를 반복하여 개별 파일을 처리합니다.

  • 마지막으로 결과를 CSV로 내보낼 것입니다.

Python 코드

이 목적으로 파이썬 코드를 사용하는 방법을 살펴 보겠습니다.

먼저 다음 Python 모듈을 가져와야합니다.

from __future__ import print_function
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

import os
import csv
import yara

다음으로 명령 줄 처리기에 대한 인수를 제공합니다. 여기서 두 개의 인수를 허용합니다. 첫 번째는 YARA 규칙의 경로이고 두 번째는 스캔 할 파일입니다.

if __name__ == '__main__':
   parser = ArgumentParser('Scanning files by YARA')
   parser.add_argument(
      'yara_rules',help = "Path to Yara rule to scan with. May be file or folder path.")
   parser.add_argument('path_to_scan',help = "Path to file or folder to scan")
   parser.add_argument('--output',help = "Path to output a CSV report of scan results")
   args = parser.parse_args()
   main(args.yara_rules, args.path_to_scan, args.output)

이제 스캔 할 yara 규칙과 파일의 경로를 수락하는 main () 함수를 정의합니다.

def main(yara_rules, path_to_scan, output):
   if os.path.isdir(yara_rules):
      yrules = yara.compile(yara_rules)
   else:
      yrules = yara.compile(filepath=yara_rules)
   if os.path.isdir(path_to_scan):
      match_info = process_directory(yrules, path_to_scan)
   else:
      match_info = process_file(yrules, path_to_scan)
   columns = ['rule_name', 'hit_value', 'hit_offset', 'file_name',
   'rule_string', 'rule_tag']
   
   if output is None:
      write_stdout(columns, match_info)
   else:
      write_csv(output, columns, match_info)

이제 디렉토리를 반복하고 추가 처리를 위해 결과를 다른 방법으로 전달하는 방법을 정의합니다.

def process_directory(yrules, folder_path):
   match_info = []
   for root, _, files in os.walk(folder_path):
      for entry in files:
         file_entry = os.path.join(root, entry)
         match_info += process_file(yrules, file_entry)
   return match_info

다음으로 두 가지 기능을 정의하십시오. 먼저 우리는match() 방법 yrulesobject 및 다른 사용자는 사용자가 출력 파일을 지정하지 않은 경우 일치하는 정보를 콘솔에보고합니다. 아래 표시된 코드를 관찰하십시오.

def process_file(yrules, file_path):
   match = yrules.match(file_path)
   match_info = []
   
   for rule_set in match:
      for hit in rule_set.strings:
         match_info.append({
            'file_name': file_path,
            'rule_name': rule_set.rule,
            'rule_tag': ",".join(rule_set.tags),
            'hit_offset': hit[0],
            'rule_string': hit[1],
            'hit_value': hit[2]
         })
   return match_info
def write_stdout(columns, match_info):
   for entry in match_info:
      for col in columns:
         print("{}: {}".format(col, entry[col]))
   print("=" * 30)

마지막으로 아래와 같이 출력을 CSV 파일에 기록하는 방법을 정의합니다.

def write_csv(outfile, fieldnames, data):
   with open(outfile, 'w', newline="") as open_outfile:
      csvfile = csv.DictWriter(open_outfile, fieldnames)
      csvfile.writeheader()
      csvfile.writerows(data)

위의 스크립트를 성공적으로 실행하면 명령 줄에서 적절한 인수를 제공하고 CSV 보고서를 생성 할 수 있습니다.