#!/usr/bin/env python #Copyright (c) 2009 Jeff Bryner #python script to gather facebook artifacts from a pd process memory dump #example: # #on windows box, use pd from www.trapkit.de ala: #pd -p 1234> 1234.dump # #where 1234 is a running instance of IE/firefox/browser # #on linux box do: #strings -el 1234.dump> memorystrings.txt #./pdfbook.v1.py -f memorystrings.txt # #It'll find what it can out of the memory image #This program is free software; you can redistribute it and/or modify it under #the terms of the GNU General Public License as published by the Free Software #Foundation; either version 2 of the License, or (at your option) any later #version. #This program is distributed in the hope that it will be useful, but WITHOUT #ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. #You should have received a copy of the GNU General Public License along with #this program; if not, write to the Free Software Foundation, Inc., #59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import sys import os import types import struct from time import * import getopt import array import re import sha safestringre=re.compile('[\x80-\xFF]') printablestringre=re.compile('[\x80-\xFF\x00-\x1F]') ipre=re.compile('(?:\d{1,3}\.){3}\d{1,3}') #used to remove the noise from the output since we're not a browser #sometimes there are double quotes, sometimes single. I'd or them with | but it messes up the matching pair. Brute force it is. onclickre=re.compile(r"""(onclick=\".*?\")""",re.IGNORECASE) onclicksinglere=re.compile(r"""(onclick=\'.*?\')""",re.IGNORECASE) onmousedownre=re.compile(r"""(onmousedown=\".*?\")""",re.IGNORECASE) onmousedownsinglere=re.compile(r"""(onmousedown=\'.*?\')""",re.IGNORECASE) spanendre=re.compile(r"""""",re.IGNORECASE) #begin #Facebook specific regexes # #story setups used to get userids and name associations. #UIIntentionalStory.setup($("div_story_50930182619061234_115316170123"), {"title":"Hide","unfollow":{"users":[{"id":543391123,"name":"Joe Facebook","firstName":"Joe","hideString":"Hide Joe"}]}}); fbookintentionalstoryre= re.compile(r"""(UIIntentionalStory.setup.*(\"id\":(.*),\"name"\:"(.*)".*"firstName"))""", re.IGNORECASE) #'what's on your mind' #"userInfos":{"1421688012":{"name":"John Doe","firstName":"John","thumbSrc":"http:\/\/profile.ak.fbcdn.net\/v228\/472\/64\/q1421688012_3296.jpg","status":"StatusText goes here.","statusTime":1249259734,"statusTimeRel":"on Sunday","enableVC":false}} fbookuserinfosre=re.compile(r"""(userInfos":{"(.*?)":{("name.*?)\})""",re.IGNORECASE) fbookrecentactivityre=re.compile(r"""UIRecentActivity_Body">(.*?)(.*?) entry from the beginning

entry since memory strings are messy # IE only stuff in the

(.{5,1000}?)Scott Bryner slept for 12 hours last night.

fbookgenericstorymessagere=re.compile(r"""

(.{5,1000}?)Remove fbookremovebuttonre=re.compile(r"""(0: #we've got an 'intentionalstory' record that should look like this: #UIIntentionalStory.setup($("div_story_50930182619061234_115316170123"), {"title":"Hide","unfollow":{"users":[{"id":543391123,"name":"Joe Facebook","firstName":"Joe","hideString":"Hide Joe"}]}}); #Not much details, but names may help prove connections, and the ID's may come in handy later. for istory in fbookintentionalstories: try: matches = fbookintentionalstoryre.search(line) sys.stdout.write("Story from friend: id:" + matches.group(3) +': Name:' + matches.group(4) + '\n') #store the ID/name reference for later. if not fbookusers.has_key(matches.group(3)): fbookusers[matches.group(3)]=matches.group(4) except: sys.stderr.write("error handing intentional story in line: " + line.strip() + '\n') for fbookui in fbookuserinfosre.finditer(line): #we've got a 'userInfos' entry from the status update that should look like this: #"userInfos":{"1421688012":{"name":"John Doe","firstName":"John","thumbSrc":"http:\/\/profile.ak.fbcdn.net\/v228\/472\/64\/q1421688012_3296.jpg","status":"StatusText goes here.","statusTime":1249259734,"statusTimeRel":"on Sunday","enableVC":false}} #group 1 is the whole thing, 2 is the userid, 3 is the name/value paring for the rest. #hey, it's almost in python dict format {"name":"value","name2":"value2"} ..lets munge the data and hope for the best. fbookuidict=fbookui.group(3).replace('false',"'false'") #'false' by itself is no good. Python needs it in double quotes. fbookuidct=fbookuidict.replace('\\"','\"') #javascript from fbook returns escaped quotes, this unescapes them. fbuiDictSource='{' + fbookuidict+'}' if options["debug"]: sys.stderr.write('debug: fbuiDictSource:' + fbuiDictSource + '\n') try: fbuiDict=dict(eval(fbuiDictSource)) #safe? conflicting stories abound on the intertubes.... sys.stdout.write ('StatusUpdate: Name: %s thumbURL: %s status: %s statusTime: %s\n' %(fbuiDict['name'],fbuiDict['thumbSrc'].replace('\\',''),fbuiDict['status'],ctime(fbuiDict['statusTime']))) #store our userid we found if not fbookusers.has_key(fbookui.group(2)): fbookusers[fbookui.group(2)]=fbuiDict['name'] except: sys.stderr.write("error handling fbookuserinfo: " + fbookui + '\n') fbookrecentactivity=fbookrecentactivityre.findall(line) if len(fbookrecentactivity)>0: #we've got a recent activity record that should look like this: #
Jeff became a fan of Fishbone.

#using regex groups and pythong finditer to get to them all #we'll hash them to weed out duplicates and store the results in a dictionary for later #it's not fool proof as there's junk in memory that we can't always weed out, but it's better than repeated stories. for m in fbookintentionalstorymessagere.finditer(filedata): #substitute out all the cruft that don't mean much since we're not privy to fbook javascript though the obvious userid in the onlick looks tempting... amessage=spanendre.sub('',onclicksinglere.sub('',onclickre.sub('',m.group(1)))) amessage=onmousedownre.sub('',amessage) amessage=onmousedownsinglere.sub('',amessage) amessage=printablestring(amessage) #hash and store it if it's new. if not storymessages.has_key(sha.new(amessage.lower()).hexdigest()): storymessages[sha.new(amessage.lower()).hexdigest()]=amessage #print "StoryMessage: " + spanendre.sub('',onclicksinglere.sub('',onclickre.sub('',m.group(1)))) #10/2009 UI redesign changed the classes..so let's do it again for m in fbookgenericstorymessagere.finditer(filedata): #substitute out all the cruft that don't mean much since we're not privy to fbook javascript though the obvious userid in the onlick looks tempting... amessage=spanendre.sub('',onclicksinglere.sub('',onclickre.sub('',m.group(1)))) amessage=onmousedownre.sub('',amessage) amessage=onmousedownsinglere.sub('',amessage) amessage=printablestring(amessage) #hash and store it if it's new. if not storymessages.has_key(sha.new(amessage.lower()).hexdigest()): storymessages[sha.new(amessage.lower()).hexdigest()]=amessage #print "StoryMessage: " + spanendre.sub('',onclicksinglere.sub('',onclickre.sub('',m.group(1)))) for m in fbookremovebuttonre.finditer(filedata): if not fbookowners.has_key(m.group(2)): fbookowners[m.group(2)]='owner' if options['debug']: sys.stderr.write('debug: removebutton' + m.group(0) +'\n') sys.stderr.write('debug: possibleOwner:' + m.group(2) + '\n') #emails? for m in fbookemailre.finditer(filedata): if options['debug']: sys.stderr.write('debug: FacebookEmail blob:' + m.group(1) + '\n' ) for a in fbookemailauthorre.finditer(m.group(1)): sys.stdout.write('FacebookEmailDetail author: ' + a.group(2) + ' url: ' + a.group(1) + '\n' ) for d in fbookemaildatere.finditer(m.group(1)): sys.stdout.write('FacebookEmailDetail Date: ' + printablestring(d.group(1)) + '\n' ) for b in fbookemailbodyre.finditer(m.group(1)): sys.stdout.write('FacebookEmailDetail Body: ' + printablestring(b.group(1)) + '\n' ) # except: # sys.stderr.write("Error handling line:" + line) #print the collection of unique recent activities for a in recentactivities: if options['verbose']: #print the hash of the item along with the item itself. sys.stdout.write('RecentActivity:'+ a + ':' +recentactivities[a] + '\n') else: sys.stdout.write('RecentActivity:' +recentactivities[a] + '\n') #print the collection of unique story messages for m in storymessages: if options['verbose']: #print the hash of the item along with the item itself. sys.stdout.write('StoryMessage:'+ m + ':' +storymessages[m] + '\n') else: sys.stdout.write('StoryMessage:' +storymessages[m] + '\n') #dump our repository of userids? if options['debug']: for userid in fbookusers: if fbookowners.has_key(userid): sys.stderr.write('FacebookUserID *owner*:' + userid + ':' + fbookusers[userid]+ '\n') else: sys.stderr.write('FacebookUserID:' + userid + ':' + fbookusers[userid]+ '\n') #see if we can figure out the likely owner of anything we found for userid in fbookusers: if fbookowners.has_key(userid): sys.stdout.write('Likely Owner of fbook memory artifacts: FacebookUserID:' + userid + ' Name:' + fbookusers[userid]+ '\n') def main(): global options options = parseOptions() gatherArtifacts() if __name__ == '__main__': main()