Subversion Repositories kim

Compare Revisions

Ignore whitespace Rev 2 → Rev 3

/trunk/kim/kim.py
12,236 → 12,33
# https://sandersonforensics.com/forum/content.php?205-Chrome-history-with-Recursive-Common-Table-Expressions
"""
 
import re
import optparse
import os
import sqlite3
import sys
import shutil
import logging
from subprocess import check_output
try:
from .logger import Logger
from .utils import *
from .project import *
except:
from logger import Logger
from utils import Utils
from project import *
 
ROOT = "https://launchpad.support.sap.com/#/notes/"
fout = open('sapnotes.txt', 'w')
snotes = set()
dnotes = {}
class KIM:
snotes = set()
dnotes = {}
log = None
utils = None
 
def get_logger(level='DEBUG'):
"""Returns a logger.
"""
log = logging.getLogger('log')
if level == 'DEBUG':
log.setLevel(logging.DEBUG)
elif level == 'INFO':
log.setLevel(logging.INFO)
elif level == 'WARNING':
log.setLevel(logging.WARNING)
elif level == 'ERROR':
log.setLevel(logging.ERROR)
else:
log.setLevel(logging.INFO)
def __init__(self):
self.log = Logger('KIM').get_logger()
self.utils = Utils()
self.log.info('Starting %s %s', name, version)
 
# Redirect log to stdout
formatter = logging.Formatter("%(levelname)7s | %(lineno)4d | %(asctime)s | %(message)s")
ch = logging.StreamHandler() # Create console handler
ch.setLevel(logging.DEBUG) # Set logging devel
ch.setFormatter(formatter) # add formatter to console handler
log.addHandler(ch) # add console handler to logger
def run(self):
ff = self.utils.get_firefox_history()
self.log.debug(ff)
 
return log
 
 
log = get_logger()
log.debug('KIM')
 
def validate(url):
chunk = url[len(ROOT):]
if not chunk[0].isnumeric():
return None
 
sep = chunk.find('/')
if sep > 0:
chunk = chunk[:sep]
 
numeric = True
for char in chunk:
if not char.isnumeric():
numeric = False
 
if numeric:
ID = int(chunk)
SAPNOTE = ROOT + str(ID)
return SAPNOTE
else:
return None
 
 
def get_chrome_profile_dir():
if sys.platform in ['linux', 'linux2']:
from subprocess import check_output
try:
cmd = "ls -d /home/$USER/.config/google-chrome/default"
PRF_DIR = check_output(cmd)
if not os.path.exists(PRF_DIR):
PRF_DIR = None
#~ p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
#~ PRF_DIR = p.communicate()[0][0:-2]
except:
PRF_DIR = None
elif sys.platform == 'win32':
import os
USERNAME = os.getenv('USERNAME')
PRF_DIR = "C:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data\\Default" % USERNAME
 
if PRF_DIR is not None:
if type(PRF_DIR) != str:
PRF_DIR = PRF_DIR.decode("utf-8")
log.debug("CHROME PROFILE DIRECTORY: %s", PRF_DIR)
else:
log.debug("CHROME PROFILE DIRECTORY: not found")
return PRF_DIR
 
 
def get_firefox_profile_dir():
if sys.platform in ['linux', 'linux2']:
import subprocess
cmd = "ls -d /home/$USER/.mozilla/firefox/*.default/"
p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
PRF_DIR = p.communicate()[0][0:-2]
PRF_DIR = PRF_DIR.decode("utf-8")
elif sys.platform == 'win32':
import os
import glob
APPDATA = os.getenv('APPDATA')
FF_PRF_DIR = "%s\\Mozilla\\Firefox\\Profiles\\" % APPDATA
PATTERN = FF_PRF_DIR + "*default*"
PRF_DIR = glob.glob(PATTERN)[0]
 
log.debug("FIREFOX PROFILE DIRECTORY: %s", PRF_DIR)
 
return PRF_DIR
 
 
def printHistoryCH(placesDB):
log.debug('Printing Chrome history')
shutil.copy(placesDB, "history")
c = sqlite3.connect("history")
cursor = c.cursor()
try:
select_statement = "SELECT urls.url, date(visits.visit_time/1000000-11644473600, 'unixepoch') FROM urls, visits WHERE urls.id = visits.url;"
cursor.execute(select_statement)
results = cursor.fetchall() #tuple
count = 0
for url, vtime in results:
if url.startswith("https://launchpad.support.sap.com/#/notes/"):
SAPNOTE = validate(url)
if SAPNOTE is not None:
line = "%s:%s" % (vtime, SAPNOTE)
log.debug("\tFound: %s", line)
snotes.add(line)
# ~ fout.write ("%s:%s\n" % (vtime, SAPNOTE))
count = count + 1
log.debug ("Chrome: %d SAP Notes found" % count)
except Exception as error:
log.error (error)
 
 
def printHistoryFF(placesDB):
log.debug('Printing Firefox history')
try:
conn = sqlite3.connect(placesDB)
c = conn.cursor()
c.execute("select url, date(visit_date/1000000, \
'unixepoch') from moz_places, moz_historyvisits \
where visit_count > 0 and moz_places.id==\
moz_historyvisits.place_id;")
 
count = 0
for row in c:
url = str(row[0])
date = str(row[1])
if url.startswith("https://launchpad.support.sap.com/#/notes/"):
SAPNOTE = validate(url)
if SAPNOTE is not None:
line = "%s\t%s" % (date, SAPNOTE)
log.debug("\tFound: %s", line)
snotes.add(line)
count = count + 1
log.debug ("Firefox: %d SAP Notes found" % count)
except Exception as e:
if 'encrypted' in str(e):
log.error ('Error reading your places database.')
log.error ('Upgrade your Python-Sqlite3 Library')
 
 
def get_firefox_history():
pathName = get_firefox_profile_dir()
if pathName is None:
log.warning ("No Firefox profile path found")
return None
elif os.path.isdir(pathName) == False:
log.warning ('Path Does Not Exist: %s' % pathName)
return None
else:
placesDB = os.path.join(pathName, 'places.sqlite')
log.debug("Firefox DB: %s", placesDB)
if os.path.isfile(placesDB):
printHistoryFF(placesDB)
else:
log.warning ('PlacesDb does not exist: %s' % placesDB)
return None
 
 
def get_chrome_history():
pathName = get_chrome_profile_dir()
if pathName == None:
log.warning ("No Chrome profile path found")
return None
elif os.path.isdir(pathName) == False:
log.warning ('Path Does Not Exist: %s' % pathName)
return None
else:
placesDB = os.path.join(pathName, 'history')
log.debug("Chrome DB: %s", placesDB)
if os.path.isfile(placesDB):
printHistoryCH(placesDB)
else:
log.warning ('PlacesDb does not exist: %s' % placesDB)
 
 
def printUniqueSAPNotes():
unique = set()
for line in snotes:
# Get SAP Note number
sep = line.rfind('/')
snid = line[sep+1:]
unique.add(snid)
# Get visited date and add it to set
sep = line.find(':')
visited = line[:sep]
try:
svdate = dnotes['visited']
if not visited in svdate:
svdate.add(visited)
except:
svdate = set()
svdate.add(visited)
dnotes[snid] = {}
dnotes[snid]['visited'] = svdate
 
 
log.debug ("Unique SAP Notes: %d" % len(unique))
 
 
 
def main():
get_firefox_history()
get_chrome_history()
for line in snotes:
fout.write("%s\n" % line)
fout.close()
log.debug ("Total SAP Notes: %d" % len(snotes))
printUniqueSAPNotes()
kim = KIM()
kim.run()
 
 
if __name__ == '__main__':
/trunk/kim/logger.py
0,0 → 1,38
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# Author: Tomás Vírseda <tomasvirseda@gmail.com>
# Version: 0.1
# License: GPLv3
# Description: KIM (Keep In Mind) is a little script to extract visited
# SAP Notes from webbrowsers (currently Firefox and Chrome)
"""
 
import logging
 
class Logger:
log = None
 
def __init__(self, name, level='DEBUG'):
self.log = logging.getLogger(name)
if level == 'DEBUG':
self.log.setLevel(logging.DEBUG)
elif level == 'INFO':
self.log.setLevel(logging.INFO)
elif level == 'WARNING':
self.log.setLevel(logging.WARNING)
elif level == 'ERROR':
self.log.setLevel(logging.ERROR)
else:
self.log.setLevel(logging.INFO)
 
## Redirect log to stdout
formatter = logging.Formatter("%(levelname)-7s | %(lineno)04d | %(name)-7s | %(asctime)s | %(message)s")
ch = logging.StreamHandler() # Create console handler and set level to debug
ch.setLevel(logging.DEBUG) # Set logging devel
ch.setFormatter(formatter) # add formatter to console handler
self.log.addHandler(ch) # add console handler to logger
 
 
def get_logger(self):
return self.log
/trunk/kim/project.py
0,0 → 1,34
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# Author: Tomás Vírseda <tomasvirseda@gmail.com>
# Version: 0.1
# License: GPLv3
# Description: KIM (Keep In Mind) is a little script to extract visited
# SAP Notes from webbrowsers (currently Firefox and Chrome)
"""
 
name = 'kim'
version = '0.1'
author = 'Tomás Vírseda'
author_email = 'tomasvirseda@gmail.com'
description = 'Extract SAP Notes from browsers history'
long_description = 'KIM (Keep In Mind) is a little script to extract ' \
'visited SAP Notes from webbrowsers (currently Firefox ' \
'and Chrome)'
url = 'http://subversion.t00mlabs.net/kim'
download_url = 'http://t00mlabs.net/downloads/kim-0.1.tar.gz'
license = 'GPLv3'
classifiers = [
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'Intended Audience :: Other Audience',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Natural Language :: English',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3',
'Topic :: Documentation',
'Topic :: Utilities'
]
/trunk/kim/utils.py
0,0 → 1,229
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
# Author: Tomás Vírseda <tomasvirseda@gmail.com>
# Version: 0.1
# License: GPLv3
# Description: KIM (Keep In Mind) is a little script to extract visited
# SAP Notes from webbrowsers (currently Firefox and Chrome)
"""
 
import os
import sys
import shutil
import sqlite3
import logging
import argparse
from subprocess import check_output
 
try:
from .logger import Logger
from .project import *
except:
from logger import Logger
from project import *
 
# Constants
ROOT = "https://launchpad.support.sap.com/#/notes/"
 
class Utils:
log = None
def __init__(self):
self.log = Logger('Utils').get_logger()
 
 
def validate(self, url):
chunk = url[len(ROOT):]
if not chunk[0].isnumeric():
return None
 
sep = chunk.find('/')
if sep > 0:
chunk = chunk[:sep]
 
numeric = True
for char in chunk:
if not char.isnumeric():
numeric = False
 
if numeric:
return chunk
else:
return None
 
 
def get_firefox_profile_dir(self):
if sys.platform in ['linux', 'linux2']:
import subprocess
cmd = "ls -d /home/$USER/.mozilla/firefox/*.default/"
p = subprocess.Popen([cmd], shell=True, stdout=subprocess.PIPE)
PRF_DIR = p.communicate()[0][0:-2]
PRF_DIR = PRF_DIR.decode("utf-8")
elif sys.platform == 'win32':
import os
import glob
APPDATA = os.getenv('APPDATA')
FF_PRF_DIR = "%s\\Mozilla\\Firefox\\Profiles\\" % APPDATA
PATTERN = FF_PRF_DIR + "*default*"
PRF_DIR = glob.glob(PATTERN)[0]
 
self.log.debug("FIREFOX PROFILE DIRECTORY: %s", PRF_DIR)
 
return PRF_DIR
 
 
def get_firefox_history(self):
profile_dir = self.get_firefox_profile_dir()
if profile_dir is None:
self.log.warning ("No Firefox profile path found")
return None
elif os.path.isdir(profile_dir) == False:
self.log.warning ('Path Does Not Exist: %s' % profile_dir)
return None
else:
placesDB = os.path.join(profile_dir, 'places.sqlite')
self.log.debug("Firefox DB: %s", placesDB)
if os.path.isfile(placesDB):
ff = self.print_firefox_history(placesDB)
return ff
else:
self.log.warning ('PlacesDb does not exist: %s' % placesDB)
return None
 
 
def get_chrome_profile_dir(self):
if sys.platform in ['linux', 'linux2']:
from subprocess import check_output
try:
cmd = "ls -d /home/$USER/.config/google-chrome/default"
PRF_DIR = check_output(cmd)
if not os.path.exists(PRF_DIR):
PRF_DIR = None
except:
PRF_DIR = None
elif sys.platform == 'win32':
import os
USERNAME = os.getenv('USERNAME')
PRF_DIR = "C:\\Users\\%s\\AppData\\Local\\Google\\Chrome\\User Data\\Default" % USERNAME
 
if PRF_DIR is not None:
if type(PRF_DIR) != str:
PRF_DIR = PRF_DIR.decode("utf-8")
self.log.debug("CHROME PROFILE DIRECTORY: %s", PRF_DIR)
else:
PRF_DIR = None
self.log.debug("CHROME PROFILE DIRECTORY: not found")
return PRF_DIR
 
 
def get_chrome_history(self):
pathName = get_chrome_profile_dir()
if pathName == None:
self.log.warning ("No Chrome profile path found")
return None
elif os.path.isdir(pathName) == False:
self.log.warning ('Path Does Not Exist: %s' % pathName)
return None
else:
placesDB = os.path.join(pathName, 'history')
self.log.debug("Chrome DB: %s", placesDB)
if os.path.isfile(placesDB):
print_chrome_history(placesDB)
else:
self.log.warning ('PlacesDb does not exist: %s' % placesDB)
return None
 
def print_firefox_history(self, placesDB):
sndict = {}
self.log.debug('Printing Firefox history')
try:
conn = sqlite3.connect(placesDB)
c = conn.cursor()
c.execute("select url, date(visit_date/1000000, \
'unixepoch') from moz_places, moz_historyvisits \
where visit_count > 0 and moz_places.id==\
moz_historyvisits.place_id;")
 
for row in c:
#~ self.log.info(row)
url = str(row[0])
date = str(row[1])
if '/support/notes/' in url:
snid = self.validate(url)
if snid is not None:
try:
sndate = sndict[snid]
if date > sndate:
sndict[snid] = date
except:
sndict[snid] = date
 
self.log.debug ("Firefox: %d SAP Notes found" % len(sndict))
return sndict
except Exception as error:
if 'encrypted' in str(error):
self.log.error ('Error reading your places database.')
self.log.error ('Upgrade your Python-Sqlite3 Library')
return None
else:
self.log.error(error)
return None
 
def print_chrome_history(self, placesDB):
self.log.debug('Printing Chrome history')
shutil.copy(placesDB, "history")
c = sqlite3.connect("history")
cursor = c.cursor()
try:
select_statement = "SELECT urls.url, date(visits.visit_time/1000000-11644473600, 'unixepoch') FROM urls, visits WHERE urls.id = visits.url;"
cursor.execute(select_statement)
results = cursor.fetchall() #tuple
count = 0
for url, vtime in results:
if url.startswith("https://launchpad.support.sap.com/#/notes/"):
SAPNOTE = validate(url)
if SAPNOTE is not None:
line = "%s:%s" % (vtime, SAPNOTE)
self.log.debug("\tFound: %s", line)
snotes.add(line)
# ~ fout.write ("%s:%s\n" % (vtime, SAPNOTE))
count = count + 1
self.log.debug ("Chrome: %d SAP Notes found" % count)
except Exception as error:
self.log.error (error)
return None
 
 
def printUniqueSAPNotes(self):
unique = set()
for line in snotes:
# Get SAP Note number
sep = line.rfind('/')
snid = line[sep+1:]
unique.add(snid)
# Get visited date and add it to set
sep = line.find(':')
visited = line[:sep]
try:
svdate = dnotes['visited']
if not visited in svdate:
svdate.add(visited)
except:
svdate = set()
svdate.add(visited)
dnotes[snid] = {}
dnotes[snid]['visited'] = svdate
 
 
 
def get_app_options(self):
parser = argparse.ArgumentParser(description='%s by %s <%s>' % (name, author, author_email))
#~ parser.add_argument('-a', '--all', action='all', help='Get SAP Notes from all browsers')
#~ parser = parser.add_mutually_exclusive_group(required=False)
#~ parser.add_argument('-f', '--firefox', action='store_true', dest='Firefox', help='Get SAP Notes from Firefox')
#~ parser.add_argument('-c', '--chrome', dest='Chrome', help='Get SAP Notes from Chrome')
parser.add_argument('-d', '--debug', dest='LOGLEVEL', help='Increase output verbosity', action='store', default='INFO')
parser.add_argument('-v', '--version', action='version', version='%s %s' % (name, version))
params = parser.parse_args()
 
return params
/trunk/setup.py
23,6 → 23,7
import subprocess
from setuptools import setup
 
from kim import project
 
with open('README') as f:
long_description = f.read()
40,18 → 41,6
'Changelog'
]),
]
 
if not os.path.isdir('mo'):
os.mkdir('mo')
for pofile in os.listdir('po'):
if pofile.endswith('po'):
lang = pofile.strip('.po')
modir = os.path.join('mo', lang)
if not os.path.isdir(modir):
os.mkdir(modir)
mofile = os.path.join(modir, 'kim.mo')
subprocess.call('msgfmt {} -o {}'.format(os.path.join('po', pofile), mofile), shell=True)
data_files.append(['share/locale/{}/LC_MESSAGES/'.format(lang), [mofile]])
return data_files
except Exception as error:
print ("ERROR: %s" % error)
73,36 → 62,22
 
 
setup(
name='kim',
version='0.1',
author='Tomás Vírseda',
author_email='tomasvirseda@gmail.com',
url='http://subversion.t00mlabs.net/kim',
description='KIM (Keep In Mind) is a little script to extract visited SAP Notes from web browsers (currently Firefox and Chrome)',
long_description=long_description,
download_url = 'http://t00mlabs.net/downloads/kim-0.1.tar.gz',
license='GPLv3',
name = project.name,
version = project.version,
author = project.author,
author_email = project.author_email,
url = project.url,
description = project.description,
long_description = project.long_description,
download_url = project.download_url,
license = project.license,
packages=['kim'],
# distutils does not support install_requires, but pip needs it to be
# able to automatically install dependencies
install_requires=[],
include_package_data=True,
data_files=data_files,
zip_safe=False,
platforms='any',
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'Intended Audience :: Other Audience',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Natural Language :: English',
'Operating System :: POSIX :: Linux',
'Programming Language :: Python :: 3',
'Topic :: Documentation',
'Topic :: Utilities'
],
classifiers = project.classifiers,
entry_points={
'console_scripts': [
'kim = kim.kim:main',