<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 第三章 用python進行調查取證 本章內容: 1. 通過Windows注冊表定位 2. 回收站調查 3. 審查PDF和DOC文件的元數據 4. 從Exif元數據中提取GPS坐標 5. 探究Skype結構 6. 從火狐的數據庫中枚舉瀏覽器結構 7. 審查移動設備結構 > 最終,你必須忘記技術。你越是進步,教導的也就越少,偉大的路是沒有真正的道路的。 > ---Ueshiba Morihei, Kaiso, Founder, Aikido ## 引文:如何解決BTK謀殺案 2005年2月,Wichita警方取證調查Randy Stone先生,揭開了一件塵封了30年的案件的神秘面紗。幾天前, KSAS電視臺提交給警方了一個他們從臭名昭著的BTK(綁定,酷刑,殺戮)的殺手那里接收到的軟盤。1974年至1991年的至少十起謀殺案中,BTK的殺手故意留下蛛絲馬跡,來嘲弄警察和受害者。2005年2月16日,BTK的殺手給電視臺郵寄了一個包含指令的軟盤,在這些指令中,磁盤包含了一個叫做`Test.A.rtf`(Regan, 2006)。然而這份文件包含了BTK殺手指令的同時,也包含了一些別的東西:元數據。在這份微軟的RTF格式文件中,包含了殺手的名字和地理位置。這幫助了案件的破獲,最終警察確認Denis Rader 就是BTK的殺手,案件最終告破。 計算機取證調查只需要好的調查員和他的好工具。調查員往往有很多挑剔的問題,但沒有工具能解決他們的問題。進入Python。在前幾章我們看到,解決復雜的問題只用極少的代碼證明了Python編程語言的實力。正如我們將在下面章節中看到的,我們能用極少數的Python代碼解決復雜的問題。讓我們開始用一些獨特的Windows注冊表來物理跟蹤用戶吧。 ## 你去哪里了?---在注冊表中分析無線接入點 Windows注冊表包含了一個存儲操作系統配置設置的層次化數據庫。隨著無線網的出現,Windows注冊表存儲了與無線連接相關的信息。了解注冊表鍵值的位置和意義可以為我們提供詳細的筆記本到過的地理位置。從Windows Vista之后,注冊表存儲每一個網絡信息在`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\NetworkList\Signatures\Unmanaged`子鍵值下。從Windows命令提示符,我們可以列出每一個網絡顯示描述GUID,網絡描述,網絡名稱和網關MAC地址。 ``` C:\Windows\system32>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged" /s HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Sign atures\Unmanaged\010103000F0000F0080000000F0000F04BCC2360E4B8F7DC8BDAF AB8AE4DAD8 62E3960B979A7AD52FA5F70188E103148 ProfileGuid REG_SZ {3B24CE70-AA79-4C9A-B9CC-83F90C2C9C0D} Description REG_SZ Hooters_San_Pedro Source REG_DWORD 0x8 DnsSuffix REG_SZ <none> FirstNetwork REG_SZ Public_Library DefaultGatewayMac REG_BINARY 00115024687F0000 ``` ## 使用WinReg讀取Windows注冊表 注冊表存儲的網關MAC地址作為REG_BINARY類型。在前面的例子中,16進制`\x00\x11\x50\x24\x68\x7F\x00\x00`表示的實際地址為`00:11:50:24:68:7F`。我們將寫一個快速的函數將`REG_BINARY`的值轉換為實際的MAC地址。在后面我們將會看到無線網絡的MAC地址是有用的。 ``` def val2addr(val): addr = "" for ch in val: addr += ("%02x "% ord(ch)) addr = addr.strip(" ").replace(" ",":")[0:17] return addr ``` 現在,讓我們來編寫一個函數從Windows注冊表鍵值中獲取每一個列出來的網絡的網絡名稱和MAC地址。為此,我們將利用`_winreg`模塊,Windows版的Python默認安裝的模塊。連接到注冊表后,我們可以使用`OpenKey()`函數打開鍵,并循環獲取鍵下面的網絡描述。對于每一個描述,包含下面子鍵:`ProfileGuid`, `Description`, `Source`, `DnsSuffix`, `FirstNetwork`, `DefaultGatewayMac`。網絡名稱和網關MAC地址在注冊表鍵列表中的第四個和第五個。現在我們可以枚舉每一個鍵,并在屏幕上面打印出來。 把所有的組合在一起,現在我們有一個腳本將打印存儲在注冊表中的先前連接的無線網絡的信息。 ``` # coding=UTF-8 import _winreg def val2addr(val): addr = "" for ch in val: addr += ("%02x "% ord(ch)) addr = addr.strip(" ").replace(" ",":")[0:17] return addr def printNets(): net = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged" key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, net) print '\n[*] Networks You have Joined.' for i in range(100): try: guid = _winreg.EnumKey(key, i) netKey = _winreg.OpenKey(key, str(guid)) (n, addr, t) = _winreg.EnumValue(netKey, 5) (n, name, t) = _winreg.EnumValue(netKey, 4) macAddr = val2addr(addr) netName = str(name) print '[+] ' + netName + ' ' + macAddr _winreg.CloseKey(netKey) except: break def main(): printNets() if __name__ == "__main__": main() ``` 在我們的目標筆記本上運行我們的腳本,我們可以看到先前連接過的無線網絡及其MAC地址。當測試腳本時,確保使用管理員權限運行的腳本,否則將無法讀取注冊表鍵值。 ``` C:\Users\investigator\Desktop\python discoverNetworks.py [*] Networks You have Joined. [+] Hooters_San_Pedro, 00:11:50:24:68:7F [+] LAX Airport, 00:30:65:03:e8:c6 [+] Senate_public_wifi, 00:0b:85:23:23:3e ``` ## 使用Mechanize 將MAC地址提交到Wigle 然而,腳本不會在次結束。隨著獲得無線接入點的MAC地址,我們現在開可以打印出無線接入點的物理位置。有相當多的數據庫,包括開源的和專有的,包含了大量與無線接入點物理位置相關的信息。專利產品,如手機就是使用這樣的地理位置的數據庫而沒有使用GPS。 SkyHook數據庫,可以在 http://www.skyhookwireless.com/ 找到。提供了一個基于WIFI接入點的軟件開發工具包。Ian McCracken開發的一個開源項目提供了提供了對這個數據庫的訪問能力在 http://code.google.com/p/maclocate/ 網站。然而,最近,SkyHook改變了SDK而是使用API密鑰來使用數據庫。Google也維護這同樣大的數據庫用于關聯無線接入點的MAC地址到物理位置。然而,不久后,不久后,Gorjan Petrovski開發了一個NMAP NSE腳本來和Google的數據庫進行交互。Google反對開源代碼和他的數據庫進行交互。不久之后,由于隱私問題,微軟也關閉了類似的WIFI物理位置數據庫。 剩下的數據庫和開源項目WiGLE.net繼續允許用戶通過無線接入點的搜索物理位置。注冊一個賬號之后,用戶就能通過一個小的Python腳本和wigle.net進行交互。讓我們快速檢查如何建立一個腳本與WiGLE.net交互。 使用WiGLE. Net,用戶很快就會意識到為了得到WiGLE他必須與第三方的頁面進行交互。首先,用戶必須打開WiGLE.net的初始化頁面在 https://wigle.net/ 網頁;然后用戶必須登陸到WiGLE在 https://wigle.net/ 頁面。最后,用戶可以查詢特定的無線SSID的MAC地址在 https://wigle.net/ 頁面。捕獲MAC地址查詢請求,我們可以看到在請求無線接入點的GPS地址的HTTP POST請求中tnetid(網絡標識符)包含了MAC地址。 ``` POST /gps/gps/main/confirmquery/ HTTP/1.1 Accept-Encoding: identity Content-Length: 33 Host: wigle.net User-Agent: AppleWebKit/531.21.10 Connection: close Content-Type: application/x-www-form-urlencoded netid=0A%3A2C%3AEF%3A3D%3A25%3A1B <..SNIPPED..> ``` 此外,我們看到從頁面響應的數據中包含了GPS坐標。字符串`maplat=47.25264359&maplon=-87.25624084`包含了接入點的經度和緯度。 ``` <tr class="search"><td> <a href="/gps/gps/Map/onlinemap2/?maplat=47.25264359&amp;maplon=- 87.25624084&amp;mapzoom=17&amp;ssid=McDonald's FREE Wifi&amp;netid=0A:2C:EF:3D: 25:1B">Get Map</a></td> <td>0A:2C:EF:3D:25:1B</td><td>McDonald's FREE Wifi</td>< ``` 有了這些信息,我們現在足夠建立建立一個簡單的函數用來返回WiGLE數據庫中記錄的無線接入點的的經度和緯度。注意,要使用`mechanize`模塊。可以從 http://wwwsearch.sourceforge.net/mechanize/ 網站獲得該模塊。`mechanize`允許通過Python進行WEB狀態編程,類似于`urllib2`模塊的功能。這就意味著,一旦我們正常的登陸到WiGLE服務,它就會存儲和重用我們的驗證cookie。 ``` import mechanize, urllib, re, urlparse def wiglePrint(username, password, netid): browser = mechanize.Browser() browser.open('http://wigle.net') reqData = urllib.urlencode({'credential_0': username, 'credential_1': password}) browser.open('https://wigle.net/gps/gps/main/login', reqData) params = {} params['netid'] = netid reqParams = urllib.urlencode(params) respURL = 'http://wigle.net/gps/gps/main/confirmquery/' resp = browser.open(respURL, reqParams).read() mapLat = 'N/A' mapLon = 'N/A' rLat = re.findall(r'maplat=.*\&amp;', resp) if rLat: mapLat = rLat[0].split('&amp;')[0].split('=')[1] rLon = re.findall(r'maplon=.*\&amp;', resp) if rLon: mapLon = rLon[0].split print('[-] Lat: ' + mapLat + ', Lon: ' + mapLon) ``` 添加WiGLE MAC地址查詢功能到我們原來的腳本。我們現在有能力檢查注冊表中以前連接過的無線接入點并查詢他們的物理位置。 ``` # coding=UTF-8 import optparse import mechanize import urllib import re import _winreg def val2addr(val): addr = "" for ch in val: addr += ("%02x " % ord(ch)) addr = addr.strip(" ").replace(" ", ":")[0:17] return addr def wiglePrint(username, password, netid): browser = mechanize.Browser() browser.open('http://wigle.net') reqData = urllib.urlencode({'credential_0': username, 'credential_1': password}) browser.open('https://wigle.net/gps/gps/main/login', reqData) params = {} params['netid'] = netid reqParams = urllib.urlencode(params) respURL = 'http://wigle.net/gps/gps/main/confirmquery/' resp = browser.open(respURL, reqParams).read() mapLat = 'N/A' mapLon = 'N/A' rLat = re.findall(r'maplat=.*\&amp;', resp) if rLat: mapLat = rLat[0].split('&amp;')[0].split('=')[1] rLon = re.findall(r'maplon=.*\&amp;', resp) if rLon: mapLon = rLon[0].split print('[-] Lat: ' + mapLat + ', Lon: ' + mapLon) def printNets(username, password): net = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\Unmanaged" key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, net) print '\n[*] Networks You have Joined.' for i in range(100): try: guid = _winreg.EnumKey(key, i) netKey = _winreg.OpenKey(key, str(guid)) (n, addr, t) = _winreg.EnumValue(netKey, 5) (n, name, t) = _winreg.EnumValue(netKey, 4) macAddr = val2addr(addr) netName = str(name) print('[+] ' + netName + ' ' + macAddr) wiglePrint(username, password, macAddr) _winreg.CloseKey(netKey) except: break def main(): parser = optparse.OptionParser("usage%prog -u <wigle username> -p <wigle password>") parser.add_option('-u', dest='username', type='string', help='specify wigle password') parser.add_option('-p', dest='password', type='string', help='specify wigle username') (options, args) = parser.parse_args() username = options.username password = options.password if username == None or password == None: print(parser.usage) exit(0) else: printNets(username, password) if __name__ == '__main__': main() ``` 運行我們的新腳本,我們可以看到先前連接過的無線網絡和他們的物理位置。知道了計算機在哪,讓我們在下一節檢查一下回收站。 ``` C:\Users\investigator\Desktop\python discoverNetworks.py [*] Networks You have Joined. [+] Hooters_San_Pedro, 00:11:50:24:68:7F [-] Lat: 29.55995369, Lon: -98.48358154 [+] LAX Airport, 00:30:65:03:e8:c6 [-] Lat: 28.04605293, Lon: -82.60256195 [+] Senate_public_wifi, 00:0b:85:23:23:3e [-] Lat: 44.95574570, Lon: -93.10277557 ``` ## 用Python來恢復回收站中刪除的項目 在微軟的操作系統中,回收站作為一個特殊的文件夾包含了已經刪除的文件。當用戶通過Windows Explorer刪除文件時,操作系統會將這個文件移動到這個特殊的文件夾中并標記這文件已刪除,但是并不是實際上的刪除它們。在Windows 98和更早的系統中用的是FAT文件系統。`C:\Recycled\`目錄保存著回收站目錄。支持NTFS的操作系統有Windows NT,2000,和XP存儲回收站在`C:\Recycler\`目錄下。Windows Vista和Windows 7系統存儲在`C:\$Recycle.Bin`目錄下。 ## 使用OS模塊查找已刪除的項目 為了讓我們的腳本不依賴與特定的Windows操作系統,讓我們編寫一個函數來測試每一個可能的候選目錄并返回系統上存在的第一個目錄。 ``` import os def returnDir(): dirs = ['C:\\Recycler\\', 'C:\\Recycled\\', 'C:\\$Recycle.Bin\\'] for recycleDir in dirs: if os.path.isdir(recycleDir): return recycleDir return None ``` 在發現回收站目錄之后,我們需要檢查里面的內容。 注意兩個子目錄,它們都包含字符串`S-1-5-21-1275210071-1715567821-725345543-`并終止與1005或者500。這個字符串用戶的SID,與機器上的用戶的賬戶一一相對應。 ``` C:\RECYCLER>dir /a Volume in drive C has no label. Volume Serial Number is 882A-6E93 Directory of C:\RECYCLER 04/12/2011 09:24 AM <DIR> . 04/12/2011 09:24 AM <DIR> .. 04/12/2011 09:56 AM <DIR> S-1-5-21-1275210071-1715567821- 725345543- 1005 04/12/2011 09:20 AM <DIR> S-1-5-21-1275210071-1715567821- 725345543- 500 0 File(s) 0 bytes 4 Dir(s) 30,700,670,976 bytes free ``` ## 用Python將用戶的SID關聯起來 我們將使用Windows注冊表將SID轉化為一個準確的用戶名。通過檢查Windows注冊表鍵值`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<SID>\ProfileImagePath`,我們可以看到它返回一個是`%SystemDrive%\Documents and Settings\<USERID>`。在下圖中,我們看到這允許我們將SID為`S-1-5-21-1275210071-1715567821-725345543-1005`轉化為用戶名“alex”。 ``` C:\RECYCLER>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ProfileList\S-1-5-21-1275210071-1715567821-725345543-1005" /v ProfileImagePath ! REG.EXE VERSION 3.0 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList \S-1-5-21-1275210071-1715567821- 725345543-1005 ProfileImagePath REG_EXPAND_SZ %SystemDrive%\Documents and Settings\alex ``` 我們想知道回收站里誰刪除了什么文件。讓我們編寫一個小的函數來將每一個SID轉化為用戶名。當我們恢復回收站中被刪除的項目時這將使我們打印更多有用的輸出。這個函數將打開注冊便檢查`ProfileImagePath`鍵值,找到其值并從中找到用戶名。 ``` import _winreg def sid2user(sid): try: key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\\" + sid) (value, type) = _winreg.QueryValueEx(key, 'ProfileImagePath') user = value.split('\\')[-1] return user except: return sid ``` 最后,我們將所有的代碼放在一起生成一個腳本,它將打印已刪除但還在回收站中的項目。 ``` # coding=UTF-8 import os import _winreg def returnDir(): dirs = ['C:\\Recycler\\', 'C:\\Recycled\\', 'C:\\$Recycle.Bin\\'] for recycleDir in dirs: if os.path.isdir(recycleDir): return recycleDir return None def sid2user(sid): try: key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\\" + sid) (value, type) = _winreg.QueryValueEx(key, 'ProfileImagePath') user = value.split('\\')[-1] return user except: return sid def findRecycled(recycleDir): dirList = os.listdir(recycleDir) for sid in dirList: files = os.listdir(recycleDir + sid) user = sid2user(sid) print('\n[*] Listing Files For User: ' + str(user)) for file in files: print('[+] Found File: ' + str(file)) def main(): recycledDir = returnDir() findRecycled(recycledDir) if __name__ == '__main__': main() ``` 在目標機器上運行我們的腳本,我們看到腳本發現了兩個用戶:Administrator和alex。它列出了回收站中每個用戶的文件。在下一節中,我們將審查一個方法,用于檢查那些包含在調查中可能有用的文件的內部內容。 ``` Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\>python dumpRecycleBin.py [*] Listing Files For User: alex [+] Found File: Notes_on_removing_MetaData.pdf [+] Found File: ANONOPS_The_Press_Release.pdf [*] Listing Files For User: Administrator [+] Found File: 192.168.13.1-router-config.txt [+] Found File: Room_Combinations.xls C:\Documents and Settings\john\Desktop> ``` ## 元數據 在本節中,我們將編寫一個腳本用來從文件中提取元數據。文件不是清晰可見的對象,元數據可以存在于文檔,電子表格,圖像,音頻和視頻等文件類型中。創作應用程序可能會存儲一些細節如文件的作者,創建和修改時間,潛在的修訂和注釋。例如,拍照手機可以標記本地的GPS在照片中或者微軟的Word應用程序可以存儲文檔的作者。檢查每一個文件是個艱難的任務,我們可以使用Python自動處理。 ## Anonymous的元數據失敗 2010年12月10日,黑客組織Anonymous發布了一份聲明稿,描述了最近一次命名為Operation Payback攻擊的背后的動機。因為對公司不支持維基解密而感到憤怒,從而對有關公司進行分布式拒絕服務攻擊(DDOS)報復。黑客發布的聲明稿沒有簽名,沒有來源。是一個PDF文件,但是發行時包含元數據。被創建文檔的程序添加進的元數據包含作者的名字Mr. Alex Tapanaris。幾天內,警方逮捕了他。 ## 使用PyPDF解析PDF元數據 讓我們快速創建一個腳本對被逮捕的黑客組織Anonymous的成員用過的文檔進行法庭調查取證。Wired.com還保留著`ANONOPS_The_Press_Release.pdf`那份文檔。我們可以從使用`wget`下載這份文檔開始。 ``` forensic:~# wget http://www.wired.com/images_blogs/threatlevel/2010/12/ANONOPS_The_ Press_Release.pdf --2012-01-19 11:43:36-- http://www.wired.com/images_blogs/threatlevel/2010/12/ANONOPS_The_ Press_Release.pdf Resolving www.wired.com... 64.145.92.35, 64.145.92.34 Connecting to www.wired.com|64.145.92.35|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 70214 (69K) [application/pdf] Saving to: 'ANONOPS_The_Press_Release.pdf.1' 100%[================================================== ================================>] 70,214 364K/s in 0.2s 2012-01-19 11:43:39 (364 KB/s) - 'ANONOPS_The_Press_Release.pdf' saved [70214/70214] ``` PyPDF是一個優秀的第三方管理PDF文件很實用的庫,可以從網站 http://pybrary.net/pyPdf/ 獲得。它提供了文檔的信息提取,分割,合并,加密和解密的能力。為了提取元數據,我們使用函數`getDocumentInfo()`。這個方法返回一個元組數組,每一個元組包含一個元數據元素和它的值。遍歷這個數組并打印PDF文件的全部元數據。 ``` import pyPdf from pyPdf import PdfFileReader def printMeta(fileName): pdfFile = PdfFileReader(file(fileName, 'rb')) docInfo = pdfFile.getDocumentInfo() print('[*] PDF MetaData For: ' + str(fileName)) for metaItem in docInfo: print('[+] ' + metaItem + ':' + docInfo[metaItem]) ``` 添加一個選項分析器來識別特定的文件,我們有一個工具可以識別嵌入到PDF中的元數據。同樣,我們可以修改我們的腳本來測試特定的元數據,例如特定的用戶。當然,這有可能幫助執法管來搜索文件來列出作者名字。 ``` # coding=UTF-8 import pyPdf from pyPdf import PdfFileReader import optparse def printMeta(fileName): pdfFile = PdfFileReader(file(fileName, 'rb')) docInfo = pdfFile.getDocumentInfo() print('[*] PDF MetaData For: ' + str(fileName)) for metaItem in docInfo: print('[+] ' + metaItem + ':' + docInfo[metaItem]) def main(): parser = optparse.OptionParser('usage %prog -F <PDF file name>') parser.add_option('-F', dest='fileName', type='string', help='specify PDF file name') (options, args) = parser.parse_args() fileName = options.fileName if fileName == None: print(parser.usage) exit(0) else: printMeta(fileName) if __name__ == '__main__': main() ``` 指定Anonymous發布的聲明高運行我們的腳本,我們可以看到同樣的元數據導致警方逮捕了Mr. Tapanaris。 ``` forensic:~# python pdfRead.py -F ANONOPS_The_Press_Release.pdf [*] PDF MetaData For: ANONOPS_The_Press_Release.pdf [+] /Author:Alex Tapanaris [+] /Producer:OpenOffice.org 3.2 [+] /Creator:Writer [+] /CreationDate:D:20101210031827+02'00' ``` ## 理解Exif元數據 (Exif是一種圖象文件格式,它的數據存儲與JPEG格式是完全相同的。實際上Exif格式就是在JPEG格式頭部插入了數碼照片的信息,包括拍攝時的光圈、快門、白平衡、ISO、焦距、日期時間等各種和拍攝條件以及相機品牌、型號、色彩編碼、拍攝時錄制的聲音以及全球定位系統(GPS)、縮略圖等。簡單地說,`Exif=JPEG+拍攝參數`。因此,你可以利用任何可以查看JPEG文件的看圖軟件瀏覽Exif格式的照片,但并不是所有的圖形程序都能處理Exif信息。) 交換圖像文件格式(Exif)標準的定義了如何存儲圖像和視頻文件的規范。如數碼相機,掃描儀和智能手機使用這個標準來保存圖像和視頻文件。Exif標準文件包含了幾個對法庭調查取證有用的信息。Phil Harvey編寫了一個實用的工具名叫exiftool(從 http://www.sno.phy.queensu.ca/~phil/exiftool/ 可獲得)能解析這些參數。檢查所有的Exif參數可能會返回幾頁的信息,所以我們只檢查部分需要的參數信息。注意Exif參數包含相機型號名稱iPhone 4S以及圖像實際的GPS經緯度坐標。這些信息在組織圖像是很有幫助的。比如說,Mac OS X應用程序iPhoto使用位置信息來整齊的排列世界地圖上的照片。然而,這些信息也被大量的惡意的使用。想象一個士兵將Exif照片放到博客或網站上,敵人可以下載所有的照片幾秒鐘之類便可以知道士兵的調動信息。在下面的章節中,我們將建立一個腳本來連接WEB網站,下載圖像,并檢查他們的Exif元數據。 ``` investigator$ exiftool photo.JPG ExifTool Version Number : 8.76 File Name : photo.JPG Directory : /home/investigator/photo.JPG File Size : 1626 kB File Modification Date/Time : 2012:02:01 08:25:37-07:00 File Permissions : rw-r--r-- File Type : JPEG MIME Type : image/jpeg Exif Byte Order : Big-endian (Motorola, MM) Make : Apple Camera Model Name : iPhone 4S Orientation : Rotate 90 CW <..SNIPPED..> GPS Altitude : 10 m Above Sea Level GPS Latitude : 89 deg 59' 59.97" N GPS Longitude : 36 deg 26' 58.57" W <..SNIPPED..> ``` ## 使用BeautifulSoup 下載圖像 可以從 www.crummy.com/software/BeautifulSoup/ 獲得`BeautifulSoup`。`BeautifulSoup` 允許我們快速的解析HTML 和XML 文檔。更新`BeautifulSoup` 到最新版本,并使用`easy_install` 獲取安裝`BeautifulSoup` 庫。 ``` investigator:~# easy_install beautifulsoup4 Searching for beautifulsoup4 Reading http://pypi.python.org/simple/beautifulsoup4/ <..SNIPPED..> Installed /usr/local/lib/python2.6/dist-packages/beautifulsoup4-4.1.0-py2.6.egg Processing dependencies for beautifulsoup4 Finished processing dependencies for beautifulsoup4 ``` 在本節中,我們將使用`BeautifulSoup` 來抓取HTML 文檔的內容來獲取文檔中所有的圖像。注意,我們使用`urllib2` 打開文檔并讀取它。接下來我們可以創造`BeautifulSoup` 對象或者一個包含不同HTML 文檔對象的解析樹。用這樣的對象,我么可以提取所有的圖像標簽,通過使用`findall('img')`函數,這個函數返回一個包含所有圖像標簽的數組。 ``` import urllib2 from bs4 import BeautifulSoup def findImages(url): print('[+] Finding images on ' + url) urlContent = urllib2.urlopen(url).read() soup = BeautifulSoup(urlContent) imgTags = soup.findAll('img') return imgTags ``` 接下來,我們需要從網站中下載每一個圖像,然后在單獨的函數中進行檢查。為了下載圖像,我們將用到`urllib2`,`urlparse` 和`os` 模塊。首先,我們從圖像標簽中提取源地址,接著我們讀取圖像的二進制內容到一個變量,最后我們以寫-二進制模式打開文件將圖像內容寫入文件。 ``` import urllib2 from urlparse import urlsplit from os.path import basename def downloadImage(imgTag): try: print('[+] Dowloading image...') imgSrc = imgTag['src'] imgContent = urllib2.urlopen(imgSrc).read() imgFileName = basename(urlsplit(imgSrc)[2]) imgFile = open(imgFileName, 'wb') imgFile.write(imgContent) imgFile.close() return imgFileName except: return '' ``` 使用Python 的圖像庫從圖像閱讀Exif 元數據為了測試圖像的內容特到Exif 元數據,我們將使用Python 圖像庫`PIL` 來處理文件,可以從 http://www.pythonware.com/products/pil/ 獲得,以增加Python 的圖像處理能力,并允許我們快速的提取與地理位置相關的元數據信息。為了測試文件元數據,我們將打開的對象作為`PIL` 圖像對象并使用函數`getexif()`。接下來我們解析Exif 數據到一個數組,通過元數據類型索引。數組完成后,我們可以搜索數組看看它是否包含有`GPSInfo` 的Exif 參數。如果它包含`GPSInfo` 參數,我們就知道對象包含GPS 元數據并打印信息到屏幕上。 ``` from PIL import Image from PIL.ExifTags import TAGS def testForExif(imgFileName): try: exifData = {} imgFile = Image.open(imgFileName) info = imgFile._getexif() if info: for (tag, value) in info.items(): decoded = TAGS.get(tag, tag) exifData[decoded] = value exifGPS = exifData['GPSInfo'] if exifGPS: print('[*] ' + imgFileName + ' contains GPS MetaData') except: Pass ``` 將所有的包裝在一起,我們的腳本現在可以連接到一個URL 地址,解析并下載所有的圖像文件,然后測試每個文件的Exif 元數據。注意`main()`函數中,我們首先獲取站點上的所有圖像的列表,然后對數組中的每一個圖像,我們將下載圖像并測試它的GPS 元數據。 ``` # coding=UTF-8 import urllib2 import optparse from bs4 import BeautifulSoup from urlparse import urlsplit from os.path import basename from PIL import Image from PIL.ExifTags import TAGS def findImages(url): print('[+] Finding images on ' + url) urlContent = urllib2.urlopen(url).read() soup = BeautifulSoup(urlContent) imgTags = soup.findAll('img') return imgTags def downloadImage(imgTag): try: print('[+] Dowloading image...') imgSrc = imgTag['src'] imgContent = urllib2.urlopen(imgSrc).read() imgFileName = basename(urlsplit(imgSrc)[2]) imgFile = open(imgFileName, 'wb') imgFile.write(imgContent) imgFile.close() return imgFileName except: return '' def testForExif(imgFileName): try: exifData = {} imgFile = Image.open(imgFileName) info = imgFile._getexif() if info: for (tag, value) in info.items(): decoded = TAGS.get(tag, tag) exifData[decoded] = value exifGPS = exifData['GPSInfo'] if exifGPS: print('[*] ' + imgFileName + ' contains GPS MetaData') except: pass def main(): parser = optparse.OptionParser('usage%prog -u <target url>') parser.add_option('-u', dest='url', type='string', help='specify urladdress') (options, args) = parser.parse_args() url = options.url if url == None: print(parser.usage) exit(0) else: imgTags = findImages(url) for imgTag in imgTags: imgFileName = downloadImage(imgTag) testForExif(imgFileName) if __name__ == '__main__': main() ``` 對目標地址測試剛剛生成的腳本,我們可以看到其中一個圖像包含GPS 元數據信息。這個能用于對個人目標的進攻偵查,我們也可以使用此腳本來確認我們自己的漏洞,在黑客攻擊前。 ``` forensics: # python exifFetch.py -u http://www.flickr.com/photos/dvids/4999001925/sizes/o [+] Finding images on http://www.flickr.com/photos/dvids/4999001925/sizes/o [+] Dowloading image... [+] Dowloading image... [+] Dowloading image... [*] 4999001925_ab6da92710_o.jpg contains GPS MetaData [+] Dowloading image... [+] Dowloading image... [+] Dowloading image... ``` ## 用Python 調查應用程序結構 這一節我們將討論應用程序結構,即兩個流行的應用程序存儲在SQLite 數據庫中的數據。SQLite 數據庫在幾個不同的應用程序中是很流行的選擇,對于local/client 存儲類型來說。尤其是WEB 瀏覽器,因為與編程語言不相關綁定。與其相對應的client/server 關系數據庫,SQLite 數據庫存儲整個數據庫在主機上作為單個文件。最初由Dr. Richard Hipp 在美國海軍工作時創立,SQLite 數據庫在許多流行的應用程序中的使用不斷的增長。被Apple,Mozilla,Google,McAfee,MicrosoftMircso,Intuit,通用電氣,DropBox,AdobeAdro 甚至是Airbus 等公司內建到應用程序中使用SQLite 數據庫格式。了解如何解析SQLite 數據庫并在法庭調查取證中使用Python 自動處理是非常有用的。下一節的開始,我們將利用流行的語音聊天客戶端Skype 來審查SQLite 數據庫。 ## 了解Skype SQLite3 數據庫 作為4.0 版本,流行的聊天工具Skype 改變了它的內部數據庫格式,使用SQLite 格式。在Windows 系統中,Skype 存儲了的一個名叫main.db 的數據庫在路徑`C:\Documents and Settings\<User>\ApplicationData\Skype\<Skypeaccount>`目錄下,在MAC OS X 系統中,相同的數據庫放在`/Users/<User>/Library/Application\ Support/Skype/<Skype-account>`目錄下。但是Skype 存儲了什么在該數據庫中?為了更好的了解Skype SQLite 數據庫信息的模式,讓我們使用`sqlite3` 命令行工具快速的連接到數據庫。連接后,我們執行命令: ``` SELECT tbl_name FROM sqlite_master WHERE type==”table”; ``` SQLite 數據庫維護了一個表名為`sqlite _master`,這個表包含了列名為`tbl_name`,用來描述數據庫中的每一個表。執行這句`SELECT` 語句允許我們查看Skype 的`main.db` 數據庫中的表。我們可以看到,該數據庫保存的表包含電話,賬戶,消息甚至是SMS 消息的信息。 ``` investigator$ sqlite3 main.db SQLite version 3.7.9 2011-11-01 00:52:41 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> SELECT tbl_name FROM sqlite_master WHERE type=="table"; DbMeta Contacts LegacyMessages Calls Accounts Transfers Voicemails Chats Messages ContactGroups Videos SMSes CallMembers ChatMembers Alerts Conversations Participants ``` 賬戶表使用Skype 應用程序賬戶的信息。它包含的列包括用戶的名字,Skype 的簡介名稱,用戶的位置,賬戶的創建日期等信息。為了查詢這些信息,我們可以創建一個SQL 語句選擇這些列。注意,數據庫存儲在UNIX 時間日期要求轉化為更友好的格式。UNIX 時間日期提供了一個簡單的測量時間方式。它將日期簡單的記錄為自1970 年1 月1 日來的秒數的整數值。SQL 函數`datatime()`可以將這種值轉化為易懂的格式。 ``` sqlite> SELECT fullname, skypename, city, country, datetime(profile_ timestamp,'unixepoch') FROM accounts; TJ OConnor|<accountname>|New York|us|22010-01-17 16:28:18 ``` ## 使用Python 的Sqlite3 自動完成Skype 數據庫查詢 連接數據庫并執行一個`SELECT` 語句很容易,我們希望能夠自動的處理數據庫中幾個不同的表和列中的額外的信息。讓我們利用`sqlite3` 庫來編寫一個小的Python 程序來完成這些。注意我們的函數`printProfile()`,它創建一個到`main.db` 數據庫的連接,創建一個連接之后,它需要一個光標提示然后執行我們先前的`SELECT` 語句,`SELECT` 語句的結果返回一個包含數組的數組。對于每個返回的結果,它包含用戶,Skype 用戶名,位置和介紹數據的索引列。我們解釋這些結果,然后漂亮的打印他們到屏幕上。 ``` # coding=UTF-8 import sqlite3 def printProfile(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT fullname, skypename, city, country, datetime(profile_timestamp,'unixepoch') FROM Accounts;") for row in c: print('[*] -- Found Account --') print('[+] User: '+str(row[0])) print('[+] Skype Username: '+str(row[1])) print('[+] Location: '+str(row[2])+','+str(row[3])) print('[+] Profile Date: '+str(row[4])) def main(): skypeDB = "main.db" printProfile(skypeDB) if __name__ == "__main__": main() ``` 運行我們的腳本,我們看到,Skype 的`main.db` 數據庫包含了一個用戶賬戶,處于隱私的問題,我們用`<accountname>`代替真正的用戶名。 ``` investigator$ python printProfile.py [*] -- Found Account -- [+] User: TJ OConnor [+] Skype Username : <accountname> [+] Location : New York, NY,us [+] Profile Date : 2010-01-17 16:28:18 ``` 讓我們通過檢查存儲的聯系人地址進一步調查Skype 的數據庫。注意,聯系表存儲信息如顯示名,Skype 用戶名,位置,移動電話,甚至是生日等每一個聯系都存儲在數據庫中。所有這些個人信息當我們調查或者是攻擊一個目標時都是有用的,所以我們將信息收集起來。讓我們輸出`SELECT` 語句返回的信息,注意幾個字段,比如生日可能是`null`,在這種情況下,我們利用條件語句只打印不等于空的結果。 ``` def printContacts(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT displayname, skypename, city, country, phone_mobile, birthday FROM Contacts;") for row in c: print('\n[*] -- Found Contact --') print('[+] User : ' + str(row[0])) print('[+] Skype Username : ' + str(row[1])) if str(row[2]) != '' and str(row[2]) != 'None': print('[+] Location : ' + str(row[2]) + ',' + str(row[3])) if str(row[4]) != 'None': print('[+] Mobile Number : ' + str(row[4])) if str(row[5]) != 'None': print('[+] Birthday : ' + str(row[5])) ``` 直到現在我們只是從特定的表中提取特定的列檢查。然而,當我們想將兩個表中的信息一起輸出怎么辦?在這種情況下,我們不得不將唯一標識結果的值加入數據庫表中。為了說明這一點,我們來探究如何輸出存儲在Skype 數據庫中的通話記錄。為了輸出詳細的Skype 通話記錄,我們需要同時使用通話表和聯系表。通話表維護著通話的時間戳和和每個通話的唯一索引字段名為`conv_dbid`。聯系表維護了通話者的身份和每一個電話的ID 列明為id。因此,為了連接兩個表我們需要查詢的`SELECT` 語句有田條件語句`WHERE calls.conv_dbid = conversations.id` 來確認。這條語句的結果返回包含所有存儲在Skype 數據庫中的Skype 的通話記錄時間和身份。 ``` def printCallLog(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT datetime(begin_timestamp,'unixepoch'), identity FROM calls, conversations WHERE calls.conv_dbid = conversations.id;") print('\n[*] -- Found Calls --') for row in c: print('[+] Time: '+str(row[0]) + ' | Partner: ' + str(row[1])) ``` 讓我們添加最后一個函數來完成我們的腳本。證據豐富,Skype 數據庫實際默認包含了所有用戶發送和接受的信息。存儲這些信息的為`Message` 表。從這個表,我們將執行`SELECT the timestamp, dialog_partner, author, and body_xml(raw text of the message)`語句。注意,如果作者來子不同的`dialog_partner`,數據庫的擁有者發送初始化信息到`dialog_partner`。否則,如果作者和`dialog_partner` 相同,`dialog_partner` 初始化這些信息,我們將從`dialog_partner` 打印。 ``` def printMessages(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT datetime(timestamp,'unixepoch'), dialog_partner, author, body_xml FROM Messages;") print('\n[*] -- Found Messages --') for row in c: try: if 'partlist' not in str(row[3]): if str(row[1]) != str(row[2]): msgDirection = 'To ' + str(row[1]) + ': ' else: msgDirection = 'From ' + str(row[2]) + ': ' print('Time: ' + str(row[0]) + ' ' + msgDirection + str(row[3])) except: pass ``` 將所有的包裝在一起,我們有一個非常強的腳本來檢查Skype 資料數據庫。我們的腳本可以打印配置文件信息,聯系人地址,通話記錄甚至是存儲在數據庫中的消息。我們可以在`main()`函數中添加一些選項解析,利用`OS` 模塊的功能確保在調查數據庫時執行每個函數之前配置文件存在。 ``` # coding=UTF-8 import sqlite3 import optparse import os def printProfile(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT fullname, skypename, city, country, datetime(profile_timestamp,'unixepoch') FROM Accounts;") for row in c: print('[*] -- Found Account --') print('[+] User: '+str(row[0])) print('[+] Skype Username: '+str(row[1])) print('[+] Location: '+str(row[2])+','+str(row[3])) print('[+] Profile Date: '+str(row[4])) def printContacts(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT displayname, skypename, city, country, phone_mobile, birthday FROM Contacts;") for row in c: print('\n[*] -- Found Contact --') print('[+] User : ' + str(row[0])) print('[+] Skype Username : ' + str(row[1])) if str(row[2]) != '' and str(row[2]) != 'None': print('[+] Location : ' + str(row[2]) + ',' + str(row[3])) if str(row[4]) != 'None': print('[+] Mobile Number : ' + str(row[4])) if str(row[5]) != 'None': print('[+] Birthday : ' + str(row[5])) def printCallLog(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT datetime(begin_timestamp,'unixepoch'), identity FROM calls, conversations WHERE calls.conv_dbid = conversations.id;") print('\n[*] -- Found Calls --') for row in c: print('[+] Time: '+str(row[0]) + ' | Partner: ' + str(row[1])) def printMessages(skypeDB): conn = sqlite3.connect(skypeDB) c = conn.cursor() c.execute("SELECT datetime(timestamp,'unixepoch'), dialog_partner, author, body_xml FROM Messages;") print('\n[*] -- Found Messages --') for row in c: try: if 'partlist' not in str(row[3]): if str(row[1]) != str(row[2]): msgDirection = 'To ' + str(row[1]) + ': ' else: msgDirection = 'From ' + str(row[2]) + ': ' print('Time: ' + str(row[0]) + ' ' + msgDirection + str(row[3])) except: pass def main(): parser = optparse.OptionParser("usage%prog -p <skype profilepath> ") parser.add_option('-p', dest='pathName', type='string', help='specify skype profile path') (options, args) = parser.parse_args() pathName = options.pathName if pathName == None: print parser.usage exit(0) elif os.path.isdir(pathName) == False: print '[!] Path Does Not Exist: ' + pathName exit(0) else: skypeDB = os.path.join(pathName, 'main.db') if os.path.isfile(skypeDB): printProfile(skypeDB) printContacts(skypeDB) printCallLog(skypeDB) printMessages(skypeDB) else: print '[!] Skype Database does not exist: ' + skypeDB if __name__ == "__main__": main() ``` 運行該腳本,我們添加一個`-p` 選項來確定Skype 配置數據庫路徑。腳本打印出存儲在目標機器上的賬戶配置,聯系人,電話和消息。成功!在下一節中,我們將用我們的`sqlite3` 的知識來調查流行的火狐瀏覽器存儲的結構。 ``` investigator$ python skype-parse.py -p /root/.Skype/not.myaccount [*] -- Found Account -- [+] User: TJ OConnor [+] Skype Username: <accountname> [+] Location: New York, US [+] Profile Date: 2010-01-17 16:28:18 [*] -- Found Contact -- [+] User: Some User [+] Skype Username : some.user [+] Location [+] Mobile Number [+] Birthday: Basking Ridge, NJ,us: +19085555555: 19750101 [*] -- Found Calls -- [+] Time: 2011-12-04 15:45:20 | Partner: +18005233273 [+] Time: 2011-12-04 15:48:23 | Partner: +18005210810 [+] Time: 2011-12-04 15:48:39 | Partner: +18004284322 [*] -- Found Messages -- Time: 2011-12-02 00:13:45 From some.user: Have you made plane reservations yets? Time: 2011-12-02 00:14:00 To some.user: Working on it... Time: 2011-12-19 16:39:44 To some.user: Continental does not have any flights available tonight. Time: 2012-01-10 18:01:39 From some.user: Try United or US Airways, they should fly into Jersey. ``` ## 其他有用的Skype 查詢 如果有興趣的話可以花時間更深入的調查Skype 數據庫,編寫新的腳本。考慮以下可能會用到的其他查詢: 想只打印出聯系人列表中的聯系人生日? ``` SELECT fullname, birthday FROM contacts WHERE birthday > 0; ``` 想打印只有特定的`<SKYPE-PARTNER>`聯系人記錄? ``` SELECT datetime(timestamp,’unixepoch’), dialog_partner, author, body_xml FROM Messages WHERE dialog_partner = ‘<SKYPE-PARTNER>’ ``` 想刪除特定的`<SKYPE-PARTNER>`聯系記錄? ``` DELETE FROM messages WHERE skypename = ‘<SKYPE-PARTNER>’ ``` ## 用Python 解析火狐Sqlite3 數據庫 在上一節中,我們研究了Skype 存儲的單一的應用數據庫。該數據庫提供了大量的調查數據。在本節中,我們將探究火狐存儲的是一系列的什么樣的數據庫。火狐存儲這些數據庫的默認目錄為`C:\Documents and Settings\<USER>\Application Data\Mozilla\Firefox\Profiles\<profile folder>\`, 在Windows 系統下,在MAC OS X 系統中存儲在 `/Users/<USER>/Library/Application\ Support/Firefox/Profiles/<profilefolder>`目錄下。讓我們列出存儲在目錄中的數據庫吧。 ``` investigator$ ls *.sqlite places.sqlite downloads.sqlite search.sqlite addons.sqlite extensions.sqlite signons.sqlite chromeappsstore.sqlite formhistory.sqlite webappsstore.sqlite content-prefs.sqlite permissions.sqlite cookies.sqlite places.sqlite ``` 檢查目錄列表,很明顯火狐存儲了相當豐富的數據。但是我們該從哪兒開始調查?讓我們從`downloads.sqlite` 數據庫開始調查。`downloads.sqlite` 文件存儲了火狐用戶下載文件的信息。它包含了一個表明為`moz_downloads`,用來存儲文件名,下載源,下載日期,文件大小,存儲在本地的位置等信息。我們使用一個Python 腳本來執行`SELECT` 語句來查詢適當的列:名稱,來源和日期時間。注意火狐用的也是UNIX 時間日期。但為了存儲UNIX 時間日期到數據庫,它將日期乘以1000000 秒,因此我們正確的時間格式應該是除以1000000 秒。 ``` import sqlite3 def printDownloads(downloadDB): conn = sqlite3.connect(downloadDB) c = conn.cursor() c.execute('SELECT name, source, datetime(endTime/1000000, \'unixepoch\') FROM moz_downloads;') print '\n[*] --- Files Downloaded --- ' for row in c: print('[+] File: ' + str(row[0]) + ' from source: ' + str(row[1]) + ' at: ' + str(row[2])) ``` 對`downloads.sqlite` 文件運行腳本,我們看到,此配置文件包含了我們以前下載文件的信息。事實上,我們是在前面學習元數據時下載的文件。 ``` investigator$ python firefoxDownloads.py [*] --- Files Downloaded --- [+] File: ANONOPS_The_Press_Release.pdf from source: http://www.wired.com/images_blogs/threatlevel/2010/12/ANONOPS_The_Press_Release.pdf at: 2011-12-14 05:54:31 ``` 好極了!我們現在知道什么時候用戶使用火狐下載過什么文件。然而,如果調查者想使用用戶的認證重新登陸到網站該怎么辦?例如,警方調查員確認用戶從基于郵件的網站上下載了對兒童有害的圖片該怎么辦?警方調查員想重新登陸到網站,最有可能的是缺少密碼或者是用戶認證的電子郵件。進入cookies,由于HTTP 西意缺乏狀態設計,網站利用cookies 來維護狀態。 考慮一下,例如,當用戶登陸到站點,如果瀏覽器不能維護cookies,用戶需要登陸能閱讀每一個人的私人郵件。火狐存儲了這些cookies 在`cookies. sqlite` 數據庫中。如調查員可以提取cookies 并重用,就提供了需要認證才能登陸到資源的條件。 讓我們快速編寫一個Python 腳本提取用戶的cookies。我們連接到數據庫并執行我們的SELECT 語句。在數據庫中,`moz_cookies` 維護這cookies,從`cookies.sqlite` 數據庫中的`moz_cookies` 表中,我們將查詢主機,名稱,cookies 的值,并在屏幕中打印出來。 ``` def printCookies(cookiesDB): try: conn = sqlite3.connect(cookiesDB) c = conn.cursor() c.execute('SELECT host, name, value FROM moz_cookies') print('\n[*] -- Found Cookies --') for row in c: host = str(row[0]) name = str(row[1]) value = str(row[2]) print('[+] Host: ' + host + ', Cookie: ' + name + ', Value: ' + value) except Exception as e: if 'encrypted' in str(e): print('\n[*] Error reading your cookies database.') print('[*] Upgrade your Python-Sqlite3 Library') ``` ## 更新sqlite3 你可能會注意到如果你嘗試用默認的`sqlite3` 打開`cookies.sqlite` 數據庫會報告文件被加密或者是這不是一個數據庫。默認安裝的Sqlite3 的版本是Sqlite3.6.22 不支持WAL 日志模式。最新版本的火狐使用 `PRAGMAjournal_mode=WAL` 模式在`cookies.sqlite` 和`places.sqlite` 數據庫中。試圖用舊版本的Sqlite3 或者是`sqlite3` 模塊會報錯。 ``` SQLite version 3.6.22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select * from moz_cookies; Error: file is encrypted or is not a database After upgrading your Sqlite3 binary and Pyton-Sqlite3 libraries to a version > 3.7, you should be able to open the newer Firefox databases. investigator:~# sqlite3.7 ~/.mozilla/firefox/nq474mcm.default/ cookies.sqlite SQLite version 3.7.13 2012-06-11 02:05:22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select * from moz_cookies; 1|backtrack-linux.org|__<..SNIPPED..> 4|sourceforge.net|sf_mirror_attempt|<..SNIPPED..> To avoid our script crashing on this unhandled error, with the cookies.sqlite and places.sqlite databases, we put exceptions to catch the encrypted database error message. To avoid receiving this error, upgrade your Python-Sqlite3 library or use the older Firefox cookies.sqlite and places.sqlite databases included on the companion Web site. ``` 為了避免我們的腳本在這個錯誤上崩潰,我們將`cookies.sqlite` 和 `places.sqlite` 數據庫放在異常處理中。為了避免這個錯誤,升級你的Pythonsqlite3 庫或使用舊版本的火狐。 調查者可能也希望列舉瀏覽歷史,火狐存儲這些數據在`places.sqlite` 數據庫中。 在這里,`moz_places` 表給我們提供了寶貴的列,包含了什么時候用戶訪問了什 么網站的信息。而我們的腳本`printHistory()`函數只考慮到`moz_places` 表,而 維基百科推薦使用`moz_places` 表和`moz_historyvisits` 表得到瀏覽器歷史。 ``` def printHistory(placesDB): try: conn = sqlite3.connect(placesDB) c = conn.cursor() c.execute("select url, datetime(visit_date/1000000, 'unixepoch') from moz_places, moz_historyvisits where visit_count > 0 and moz_places.id==moz_historyvisits.place_id;") print('\n[*] -- Found History --') for row in c: url = str(row[0]) date = str(row[1]) print '[+] ' + date + ' - Visited: ' + url except Exception as e: if 'encrypted' in str(e): print('\n[*] Error reading your places database.') print('[*] Upgrade your Python-Sqlite3 Library') exit(0) ``` 讓我們使用最后的知識和先前的正則表達式的知識擴展我們的函數。瀏覽歷史及其有價值,對深入了解一些特定的URL 很有用。Google 搜索查詢包含搜索URL 內部的權限,比如說,在無線的章節里,我們將就此展開深入。然而,現在讓我們只提取搜索條件URL 右邊的條目。如果在我們的歷史里發現包含Google,我們發現他的特點`q=`后面跟隨者`&`。這個特定的字符序列標識Google 搜索。如果我們真的找到這個條目,我們將通過用空格替換一些URL 中用的字符來清理輸出。最后,我們將打印校正后的輸出到屏幕上,現在我們有一個函數可以搜索`places.sqlite` 文件并打印Google 搜索查詢歷史。 ``` import sqlite3, re def printGoogle(placesDB): conn = sqlite3.connect(placesDB) c = conn.cursor() c.execute("select url, datetime(visit_date/1000000, 'unixepoch') from moz_places, moz_historyvisits where visit_count > 0 and moz_places.id==moz_historyvisits.place_id;") print('\n[*] -- Found Google --') for row in c: url = str(row[0]) date = str(row[1]) if 'google' in url.lower(): r = re.findall(r'q=.*\&', url) if r: search=r[0].split('&')[0] search=search.replace('q=', '').replace('+', ' ') print('[+] '+date+' - Searched For: ' + search) ``` 將所有的包裝在一起,我們現在有下載文件信息,讀取cookies 和瀏覽歷史,甚至是用戶的Google 的搜索歷史功能。該選項的解析應該看看前面非常相似的Skype 數據庫的探究。 你可能注意到使用`os.join.path` 函數來創建完整的路徑會問為什么不是只添加路徑和文件的字符串值在一起。是什么讓我們不這樣使用讓我們來看一個例子: ``` downloadDB = pathName + “\\downloads.sqlite”替換 downloadDB = os.path.join(pathName,“downloads.sqlite”) ``` 考慮一下,Windows 用戶使用 `C:\Users\<user_name>\`來表示路徑,而Linux 和Mac OS 使用`/home/<user_name>/`來表示用戶路徑,不同的操作系統中,斜杠表示的意義不一樣,這點當我們創建文件的完整路徑時不得不考慮。`OS` 庫允許我們創建一個獨立于操作系統都能工作的腳本。 ``` # coding=UTF-8 import sqlite3 import re import optparse import os def printDownloads(downloadDB): conn = sqlite3.connect(downloadDB) c = conn.cursor() c.execute('SELECT name, source, datetime(endTime/1000000, \'unixepoch\') FROM moz_downloads;') print '\n[*] --- Files Downloaded --- ' for row in c: print('[+] File: ' + str(row[0]) + ' from source: ' + str(row[1]) + 'at: ' + str(row[2])) def printCookies(cookiesDB): try: conn = sqlite3.connect(cookiesDB) c = conn.cursor() c.execute('SELECT host, name, value FROM moz_cookies') print('\n[*] -- Found Cookies --') for row in c: host = str(row[0]) name = str(row[1]) value = str(row[2]) print('[+] Host: ' + host + ', Cookie: ' + name + ', Value: ' + value) except Exception as e: if 'encrypted' in str(e): print('\n[*] Error reading your cookies database.') print('[*] Upgrade your Python-Sqlite3 Library') def printHistory(placesDB): try: conn = sqlite3.connect(placesDB) c = conn.cursor() c.execute("select url, datetime(visit_date/1000000, 'unixepoch') from moz_places, moz_historyvisits where visit_count > 0 and moz_places.id==moz_historyvisits.place_id;") print('\n[*] -- Found History --') for row in c: url = str(row[0]) date = str(row[1]) print '[+] ' + date + ' - Visited: ' + url except Exception as e: if 'encrypted' in str(e): print('\n[*] Error reading your places database.') print('[*] Upgrade your Python-Sqlite3 Library') exit(0) def printGoogle(placesDB): conn = sqlite3.connect(placesDB) c = conn.cursor() c.execute("select url, datetime(visit_date/1000000, 'unixepoch') from moz_places, moz_historyvisits where visit_count > 0 and moz_places.id==moz_historyvisits.place_id;") print('\n[*] -- Found Google --') for row in c: url = str(row[0]) date = str(row[1]) if 'google' in url.lower(): r = re.findall(r'q=.*\&', url) if r: search=r[0].split('&')[0] search=search.replace('q=', '').replace('+', ' ') print('[+] '+date+' - Searched For: ' + search) def main(): parser = optparse.OptionParser("usage%prog -p <firefox profile path> ") parser.add_option('-p', dest='pathName', type='string', help='specify skype profile path') (options, args) = parser.parse_args() pathName = options.pathName if pathName == None: print(parser.usage) exit(0) elif os.path.isdir(pathName) == False: print('[!] Path Does Not Exist: ' + pathName) exit(0) else: downloadDB = os.path.join(pathName, 'downloads.sqlite') if os.path.isfile(downloadDB): printDownloads(downloadDB) else: print('[!] Downloads Db does not exist: '+downloadDB) cookiesDB = os.path.join(pathName, 'cookies.sqlite') if os.path.isfile(cookiesDB): printCookies(cookiesDB) else: print('[!] Cookies Db does not exist:' + cookiesDB) placesDB = os.path.join(pathName, 'places.sqlite') if os.path.isfile(placesDB): printHistory(placesDB) printGoogle(placesDB) else: print('[!] PlacesDb does not exist: ' + placesDB) if __name__ == "__main__": main() ``` 運行我們的腳本調查火狐用戶的配置文件,我們可以看到這些結果。在下一節中,我們將使用部分我們前面學到的技巧,但是通過在數據庫的干草堆中搜索一根針來擴展我們的SQLite 知識。 ``` investigator$ python parse-firefox.py -p ~/Library/Application\Support/Firefox/Profiles/5ab3jj51.default/ [*] --- Files Downloaded --- [+] File: ANONOPS_The_Press_Release.pdf from source: http://www.wired.com/images_blogs/threatlevel/2010/12/ANONOPS_The_Press_Release.pdf at: 2011-12-14 05:54:31 [*] -- Found Cookies -- [+] Host: .mozilla.org, Cookie: wtspl, Value: 894880 [+] Host: www.webassessor.com, Cookie: __utma, Value: 1.224660440401.13211820353.1352185053.131218016553.1 [*] -- Found History -- [+] 2011-11-20 16:28:15 - Visited: http://www.mozilla.com/en-US/firefox/8.0/firstrun/ [+] 2011-11-20 16:28:16 - Visited: http://www.mozilla.org/en-US/firefox/8.0/firstrun/ [*] -- Found Google -- [+] 2011-12-14 05:33:57 - Searched For: The meaning of life? [+] 2011-12-14 05:52:40 - Searched For: Pterodactyl [+] 2011-12-14 05:59:50 - Searched For: How did Lost end? ``` ## 用Python 調查移動設備的iTunes 備份 在2011 年4 月,安全研究人員和前蘋果員工公開了iPhone 和Ipad IOS 操作系統的一個隱私問題。一個重要的調查之后發現IOS 系統事實上跟蹤和記錄設備的GPS 坐標并存儲在手機的`consolidated.db` 數據庫中。在這個數據庫中一個名為 `Cell-Location` 的表包含了收集的手機的GPS 坐標。該設備通過綜合了最近的手機信號發射塔來確定定位信息為用戶提供更好的服務。然而,安全人員提出,該收據可能會被惡意的使用,用來跟蹤iPhone/Ipad 用戶的完整活動路線。此外,使用備份和存儲移動設備的信息到電腦上也記錄了這些信息。雖然定位記錄信息已經從蘋果系統中移出了,但發現數據的過程任然還在。在本節中,我們將重復這一過程,從iPhone 設備中提取備份信息。具體來說,我們將使用Python 腳本從IOS 備份中提取所有的文本消息。 當用戶對iPhone 或者iPad 設備進行備份時,它將文件存儲到機器的特殊目錄。對于Windows 系統,iTunes 程序存儲移動設備備份目錄在 `C:\Documents and Settings\<USERNAME>\Application Data\AppleComputer\MobileSync\Backup` 下,在Mac OS X 系統上儲存目錄在 `/Users/<USERNAME>/Library/Application Support/MobileSync/Backup/`。iTunes 程序備份移動設備存儲所有的移動設備到這些目錄下。讓我們來探究我的iPhone 最近的備份文件。 ``` investigator$ ls 68b16471ed678a3a470949963678d47b7a415be3 68c96ac7d7f02c20e30ba2acc8d91c42f7d2f77f 68b16471ed678a3a470949963678d47b7a415be3 68d321993fe03f7fe6754f5f4ba15a9893fe38db 69005cb27b4af77b149382d1669ee34b30780c99 693a31889800047f02c64b0a744e68d2a2cff267 6957b494a71f191934601d08ea579b889f417af9 698b7961028238a63d02592940088f232d23267e 6a2330120539895328d6e84d5575cf44a082c62d <..SNIPPED..> ``` 為了獲得關于每個文件更多的信息,我們將使用UNIX 命令`file` 來提取每個文件的文件類型。這個命令使用文件頭的字節信息類確認文件類型。這為我們提供了更多的信息,我們看到移動備份目錄包含了一些`sqlite3` 數據庫,JPEG 圖像,原始數據和ASCII 文本文件。 ``` investigator$ file * 68b16471ed678a3a470949963678d47b7a415be3: data 68c96ac7d7f02c20e30ba2acc8d91c42f7d2f77f: SQLite 3.x database 68b16471ed678a3a470949963678d47b7a415be3: JPEG image data 68d321993fe03f7fe6754f5f4ba15a9893fe38db: JPEG image data 69005cb27b4af77b149382d1669ee34b30780c99: JPEG image data 693a31889800047f02c64b0a744e68d2a2cff267: SQLite 3.x database 6957b494a71f191934601d08ea579b889f417af9: SQLite 3.x database 698b7961028238a63d02592940088f232d23267e: JPEG image data 6a2330120539895328d6e84d5575cf44a082c62d: ASCII English text <..SNIPPED..> ``` `file` 命令讓我們知道一些文件包含SQLite 數據庫并對滅個數據庫的內容有少量的描述。我們將使用Python 腳本快速的快速的枚舉在移動備份目錄下找到的每一個數據庫的所有的表。注意我們將再次在我們的Python 腳本中使用`sqlite3`。我們的腳本列出工作目錄的內容然后嘗試連接每一個數據庫。對于那些成功的連接,腳本將執行命令:`SELECT tbl_name FROM sqlite_masterWHERE type==‘table’`。,每一個SQLite 數據庫維護了一個`sqlite_master` 的表包含了數據庫的總體結構,說明了數據庫的總體架構。上面的命令允許我們列舉數據庫模式。 ``` import os import sqlite3 def printTables(iphoneDB): try: conn = sqlite3.connect(iphoneDB) c = conn.cursor() c.execute('SELECT tbl_name FROM sqlite_master WHERE type==\"table\";') print("\n[*] Database: "+iphoneDB) for row in c: print("[-] Table: "+str(row)) except: pass finally: conn.close() dirList = os.listdir(os.getcwd()) for fileName in dirList: printTables(fileName) ``` 運行我們的腳本,我們列舉了移動備份目錄里的所有的數據庫模式。當腳本找到多個數據庫,我們將整理輸出我們關心的特定的數據庫。注意文件名為`d0d7e5fb2ce288813306e4d4636395e047a3d28` 包含了一個SQLite 數據庫里面有一個名為`messages` 的表。該數據庫包含了存儲在iPhone 備份中的文本消息列表。 ``` investigator$ python listTables.py <..SNIPPED...> [*] Database: 3939d33868ebfe3743089954bf0e7f3a3a1604fd [-] Table: (u'ItemTable',) [*] Database: d0d7e5fb2ce288813306e4d4636395e047a3d28 [-] Table: (u'_SqliteDatabaseProperties',) [-] Table: (u'message',) [-] Table: (u'sqlite_sequence',) [-] Table: (u'msg_group',) [-] Table: (u'group_member',) [-] Table: (u'msg_pieces',) [-] Table: (u'madrid_attachment',) [-] Table: (u'madrid_chat',) [*] Database: 3de971e20008baa84ec3b2e70fc171ca24eb4f58 [-] Table: (u'ZFILE',) [-] Table: (u'Z_1LABELS',) <..SNIPPED..> ``` 雖然現在我們知道SQLite 數據庫文件`d0d7e5fb2ce288813306e4d4636395e047a3d28` 包含了文本消息,我們想要能夠自動的對不同的備份進行調查。為了執行這個,我們編寫了一個簡單的函數名為`isMessageTable()`,這個函數將連接數據庫并枚舉數據庫模式信息。如果文件包含名為`messages` 的表,則返回`True`,否則函數返回`False`。現在我們有能力快速掃描目錄下的上千個文件并確認包含`messages` 表的特定數據庫。 ``` def isMessageTable(iphoneDB): try: conn = sqlite3.connect(iphoneDB) c = conn.cursor() c.execute('SELECT tbl_name FROM sqlite_master WHERE type==\"table\";') for row in c: if 'message' in str(row): return True except: return False ``` 現在,我們可以定位文本消息數據庫了,我們希望可以打印包含在數據庫中的內容,如時間,地址,文本消息。為此,我們連接數據庫并執行以下命令:`select datetime(date,\‘unixepoch\’), address, text from message WHERE address>0;`我們可以打印查詢結果到屏幕上。注意,我們將使用一些異常處理,如果`isMessageTable()`返回的數據庫不是我們需要的文本信息數據庫,它將不包含數據,地址,和文本的列。如果我們去錯了數據庫,我們將允許腳本捕獲異常并繼續執行,直到找到正確的數據庫。 ``` def printMessage(msgDB): try: conn = sqlite3.connect(msgDB) c = conn.cursor() c.execute('select datetime(date,\'unixepoch\'),address, text from message WHERE address>0;') for row in c: date = str(row[0]) addr = str(row[1]) text = row[2] print('\n[+] Date: '+date+', Addr: '+addr + ' Message: ' + text) except: pass ``` 包裝這些函數在一起,我們可以構建最終的腳本。我們將添加一個選項解析來執行iPhone 備份的目錄。接下來,我們將列出該目錄的內容并測試每一個文件直到找到文本信息數據庫。一旦我們找到這個文件,我們可以打印數據庫的內容在屏幕上。 ``` # coding=UTF-8 import os import sqlite3 import optparse def isMessageTable(iphoneDB): try: conn = sqlite3.connect(iphoneDB) c = conn.cursor() c.execute('SELECT tbl_name FROM sqlite_master WHERE type==\"table\";') for row in c: if 'message' in str(row): return True except: return False def printMessage(msgDB): try: conn = sqlite3.connect(msgDB) c = conn.cursor() c.execute('select datetime(date,\'unixepoch\'),address, text from message WHERE address>0;') for row in c: date = str(row[0]) addr = str(row[1]) text = row[2] print('\n[+] Date: '+date+', Addr: '+addr + ' Message: ' + text) except: pass def main(): parser = optparse.OptionParser("usage%prog -p <iPhone Backup Directory> ") parser.add_option('-p', dest='pathName', type='string',help='specify skype profile path') (options, args) = parser.parse_args() pathName = options.pathName if pathName == None: print parser.usage exit(0) else: dirList = os.listdir(pathName) for fileName in dirList: iphoneDB = os.path.join(pathName, fileName) if isMessageTable(iphoneDB): try: print('\n[*] --- Found Messages ---') printMessage(iphoneDB) except: pass if __name__ == '__main__': main() ``` 對iPhone 備份目錄運行這個腳本,我們可以看到一些存儲在iPhone 備份中的最近的文本消息。 ``` investigator$ python iphoneMessages.py -p ~/Library/Application\Support/MobileSync/Backup/192fd8d130aa644ea1c644aedbe23708221146a8/ [*] --- Found Messages --- [+] Date: 2011-12-25 03:03:56, Addr: 55555554333 Message: Happy holidays, brother. [+] Date: 2011-12-27 00:03:55, Addr: 55555553274 Message: You didnt respond to my message, are you still working on the book? [+] Date: 2011-12-27 00:47:59, Addr: 55555553947 Message: Quick question, should I delete mobile device backups on iTunes? <..SNIPPED..> ``` ## 本章總結 再次祝賀!在本章調查數字結構時我們已經編寫了不少工具了。通過調查Windows 注冊表和回收站,藏在元數據中的結構,應用程序存儲的數據庫我們又增加了一些有用的工具到我們的工具庫中。希望你建立在本章的例子基礎回答你將來調查中的問題。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看