/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', |