From 4db302b048a69a2111e4fcfd4d966625609c6d4c Mon Sep 17 00:00:00 2001
From: Oleksii Shevchuk
Date: Tue, 11 Apr 2017 21:34:00 +0300
Subject: [PATCH] Support termination and preserve memory during strings dump
---
pupy/modules/memstrings.py | 122 +++++++++++++++++++-------------
pupy/packages/all/memstrings.py | 35 ++++++---
2 files changed, 95 insertions(+), 62 deletions(-)
diff --git a/pupy/modules/memstrings.py b/pupy/modules/memstrings.py
index db2582ec..feb6aba3 100644
--- a/pupy/modules/memstrings.py
+++ b/pupy/modules/memstrings.py
@@ -12,6 +12,8 @@ class MemStrings(PupyModule):
"""
dependencies=['memorpy', 'memstrings']
+ termevent = None
+
def init_argparse(self):
self.arg_parser = PupyArgumentParser(prog='memstrings', description=self.__doc__)
action = self.arg_parser.add_mutually_exclusive_group(required=True)
@@ -19,7 +21,7 @@ class MemStrings(PupyModule):
help='Include processes with specified pids')
action.add_argument('-n', '--name', nargs='*', default=[],
help='Include processes with specified names')
- self.arg_parser.add_argument('-o', '--omit', type=str, default='isrx',
+ self.arg_parser.add_argument('-x', '--omit', type=str, default='isrx',
help='Avoid scanning: '
'i - ranges with file mapping; '
's - ranges with shared region; '
@@ -29,9 +31,12 @@ class MemStrings(PupyModule):
help='Show only strings which are longer then specified length')
self.arg_parser.add_argument('-m', '--max-length', type=int, default=51,
help='Show only strings which are shorter then specified length')
-
+ self.arg_parser.add_argument('-P', '--portions', type=int, default=8192,
+ help='Strings portion block')
+ self.arg_parser.add_argument('-d', '--no-duplication', default=False, action='store_true',
+ help='Enable strings deduplication (will increase memory usage)')
self.arg_parser.add_argument(
- '-log',
+ '-o', '--output',
help='Save output to file. Omit output to stdout. You can use vars: '
'%%h - host, %%m - mac, %%P - platform, %%u - user, %%a - ip address'
'%%p - pid, %%n - name'
@@ -39,62 +44,70 @@ class MemStrings(PupyModule):
def run(self, args):
targets = args.pid + args.name
- dump = self.client.conn.modules.memstrings.find_strings(
- targets,
- min_length=args.min_length,
- max_length=args.max_length,
- omit=args.omit
- )
- dump = obtain(dump)
- if not dump:
- self.error('No dumps received')
- return
- self.success('Get {} dumps'.format(len(dump)))
+ self.termevent = self.client.conn.modules.threading.Event()
- log = None
+ logs = {}
- for pid, items in dump.iteritems():
- name = items.get('name')
- strings = items.get('strings')
+ for pid, name, strings in self.client.conn.modules.memstrings.iterate_strings(
+ targets,
+ min_length=args.min_length,
+ max_length=args.max_length,
+ omit=args.omit,
+ portions=args.portions,
+ terminate=self.termevent,
+ nodup=args.no_duplication,
+ ):
- if args.log:
- log = args.log.replace(
- '%m', self.client.desc['macaddr']
- ).replace(
- '%P', self.client.desc['platform']
- ).replace(
- '%a', self.client.desc['address']
- ).replace(
- '%h', self.client.desc['hostname'].replace(
- '..', '__'
+ strings = obtain(strings)
+ pid = str(pid) or '0'
+ name = str(name) or ''
+
+ if not strings:
+ self.error('No dumps received')
+ return
+
+ if args.output:
+ if not pid in logs:
+ log = args.output.replace(
+ '%m', self.client.desc['macaddr']
).replace(
- '/', '_'
+ '%P', self.client.desc['platform']
+ ).replace(
+ '%a', self.client.desc['address']
+ ).replace(
+ '%h', self.client.desc['hostname'].replace(
+ '..', '__'
+ ).replace(
+ '/', '_'
+ )
+ ).replace(
+ '%u', self.client.desc['user'].replace(
+ '..', '__'
+ ).replace(
+ '/', '_'
+ )
+ ).replace(
+ '%p', str(pid),
+ ).replace(
+ '%n', name.replace(
+ '..', '__'
+ ).replace(
+ '/', '_'
+ ),
)
- ).replace(
- '%u', self.client.desc['user'].replace(
- '..', '__'
- ).replace(
- '/', '_'
- )
- ).replace(
- '%p', pid,
- ).replace(
- '%n', name.replace(
- '..', '__'
- ).replace(
- '/', '_'
- ),
- )
- dirname = os.path.dirname(log)
- if not os.path.exists(dirname):
- os.makedirs(dirname)
+ dirname = os.path.dirname(log)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
- self.success('Dump {}:{} to {}'.format(name, pid, log))
- with open(log, 'w') as log:
- for s in strings:
- log.write(s+'\n')
+ self.success('Dumping {}:{} -> {}'.format(name, pid, log))
+ logs[pid] = open(log, 'a+')
+
+ for s in strings:
+ logs[pid].write(s+'\n')
+
+ logs[pid].flush()
else:
self.success('Strings {}:{}'.format(name, pid))
@@ -102,3 +115,10 @@ class MemStrings(PupyModule):
self.stdout.write(s+'\n')
self.stdout.write('\n')
+
+ for log in logs.itervalues():
+ log.close()
+
+ def interrupt(self):
+ if self.termevent:
+ self.termevent.set()
diff --git a/pupy/packages/all/memstrings.py b/pupy/packages/all/memstrings.py
index 5732857e..0c3b19e9 100644
--- a/pupy/packages/all/memstrings.py
+++ b/pupy/packages/all/memstrings.py
@@ -11,9 +11,9 @@ def try_int(x):
except:
return x
-def find_strings(targets, min_length=4, max_length=51, omit='isxr'):
+def iterate_strings(targets, min_length=4, max_length=51, omit='isxr', portions=4096, nodup=True, terminate=None):
if not targets:
- return {}
+ return
if type(targets) == (str, int):
targets = [ targets ]
@@ -24,26 +24,39 @@ def find_strings(targets, min_length=4, max_length=51, omit='isxr'):
printable = re.compile('^[\x20-\x7e]{{{},{}}}$'.format(min_length, max_length))
for process in memorpy.Process.list():
+ if terminate is not None and terminate.is_set():
+ break
+
if not (
os.path.basename(process.get('name')) in targets or process.get('pid') in targets
):
continue
strings = []
- results[process.get('pid')] = {
- 'name': process.get('name'),
- 'strings': strings
- }
+ pid = process.get('pid')
+ name = process.get('name')
mw = memorpy.MemWorker(pid=process.get('pid'))
duplicates = set()
for _, (cstring,) in mw.mem_search('([^\x00]+)', ftype='groups', optimizations=omit):
- if printable.match(cstring):
- if not cstring in duplicates:
- duplicates.add(cstring)
- strings.append(cstring)
+ if terminate is not None and terminate.is_set():
+ break
- return results
+ if printable.match(cstring):
+ if nodup:
+ if cstring in duplicates:
+ continue
+
+ duplicates.add(cstring)
+
+ strings.append(cstring)
+ if len(strings) >= portions:
+ yield pid, name, strings
+ strings = []
+
+ if strings:
+ yield pid, name, strings
+ strings = []
if __name__=="__main__":
import sys