diff --git a/.gitignore b/.gitignore index d5b6a3a..ab653eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.pyc -doc/build \ No newline at end of file +doctrees +*.inv +*.pickle \ No newline at end of file diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..b2d676d --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 2dfc3e603de84cfef16e4ba472fec71f +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 0000000..7cd6c30 --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,107 @@ + + + + + + + + Overview: module code — PythonForWindows 0.2 documentation + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/debug/breakpoints.html b/docs/build/html/_modules/windows/debug/breakpoints.html new file mode 100644 index 0000000..8a13573 --- /dev/null +++ b/docs/build/html/_modules/windows/debug/breakpoints.html @@ -0,0 +1,248 @@ + + + + + + + + windows.debug.breakpoints — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.debug.breakpoints

+from collections import OrderedDict
+
+import windows
+from windows.generated_def.winstructs import *
+from windows.generated_def import windef
+from windows.winobject.process import WinProcess, WinThread
+
+
+STANDARD_BP = "BP"
+HARDWARE_EXEC_BP = "HXBP"
+MEMORY_BREAKPOINT = "MEMBP"
+
+
[docs]class Breakpoint(object): + """An standard (Int3) breakpoint (type == ``STANDARD_BP``)""" + type = STANDARD_BP # REAL BP + def __init__(self, addr): + self.addr = addr + + def apply_to_target(self, target): + return isinstance(target, WinProcess) + +
[docs] def trigger(self, dbg, exception): + """Called when breakpoint is hit""" + pass
+ + +class ProxyBreakpoint(Breakpoint): + def __init__(self, target, addr, type): + self.target = target + self.addr = addr + self.type = type + + def trigger(self, dbg, exception): + return self.target(dbg, exception) + + +
[docs]class HXBreakpoint(Breakpoint): + """An hardware-execution breakpoint (type == ``HARDWARE_EXEC_BP``)""" + type = HARDWARE_EXEC_BP + + def apply_to_target(self, target): + return isinstance(target, WinThread)
+ +
[docs]class MemoryBreakpoint(Breakpoint): + """A memory breakpoint (type == ``MEMORY_BREAKPOINT``)""" + type = MEMORY_BREAKPOINT + DEFAULT_EVENTS = "RWX" + DEFAULT_SIZE = 0x1000 +
[docs] def __init__(self, addr, size=None, events=None): + """``size``: the size of the memory breakpoint. + + ``events``: a string representing the events that interest the BP (any of "RWX")""" + super(MemoryBreakpoint, self).__init__(addr) + self.size = size if size is not None else self.DEFAULT_SIZE + events = events if events is not None else self.DEFAULT_EVENTS + self.events = set(events)
+ +
[docs] def trigger(self, dbg, exception): + """Called when breakpoint is hit""" + pass
+ + +## Arguments Helper (need to move this elsewhere) +class X86ArgumentRetriever(object): + def get_arg(self, nb, proc, thread): + return proc.read_dword(thread.context.sp + 4 + (4 * nb)) + +class X64ArgumentRetriever(object): + REG_ARGS = ["Rcx", "Rdx", "R8", "R9"] + def get_arg(self, nb, proc, thread): + if nb < len(self.REG_ARGS): + return getattr(thread.context, self.REG_ARGS[nb]) + return proc.read_dword(thread.context.sp + 8 + (8 * nb)) + +## Behaviour breakpoint ! +class FunctionParamDumpBP(Breakpoint): + def __init__(self, target, addr=None): + if addr is None: + addr = "{0}!{1}".format(target.target_dll, target.target_func) + super(FunctionParamDumpBP, self).__init__(addr) + self.target = target + self.target_args = target.prototype._argtypes_ + + def extract_arguments_32bits(self, cproc, cthread): + x = windows.debug.X86ArgumentRetriever() + res = OrderedDict() + for i, (name, type) in enumerate(zip(self.target.params, self.target_args)): + value = x.get_arg(i, cproc, cthread) + rt = windows.remotectypes.transform_type_to_remote32bits(type) + if issubclass(rt, windows.remotectypes.RemoteValue): + t = rt(value, cproc) + else: + t = rt(value) + if not hasattr(t, "contents"): + try: + t = t.value + except AttributeError: + pass + res[name[1]] = t + return res + + def extract_arguments_64bits(self, cproc, cthread): + x = windows.debug.X64ArgumentRetriever() + res = OrderedDict() + for i, (name, type) in enumerate(zip(self.target.params, self.target_args)): + value = x.get_arg(i, cproc, cthread) + rt = windows.remotectypes.transform_type_to_remote64bits(type) + if issubclass(rt, windows.remotectypes.RemoteValue): + t = rt(value, cproc) + else: + t = rt(value) + if not hasattr(t, "contents"): + try: + t = t.value + except AttributeError: + pass + res[name[1]] = t + return res + + def extract_arguments(self, cproc, cthread): + """Extracts the functions parameters in an :class:`OrderedDict`""" + if windows.current_process.bitness == 32: + return self.extract_arguments_32bits(cproc, cthread) + if cproc.bitness == 64: + return self.extract_arguments_64bits(cproc, cthread) + # SysWow process from a 64bits debugger, handle bitness with CS + if cthread.context.SegCs == windows.syswow64.CS_32bits: + return self.extract_arguments_32bits(cproc, cthread) + return self.extract_arguments_64bits(cproc, cthread) + + +class FunctionRetBP(Breakpoint): + def __init__(self, addr, initial_breakpoint): + super(FunctionRetBP, self).__init__(addr) + self.initial_breakpoint = initial_breakpoint + + def trigger(self, dbg, exc): + dbg.del_bp(self, targets=[dbg.current_process]) + return self.initial_breakpoint.ret_trigger(dbg, exc) + + +class FunctionCallBP(Breakpoint): + def break_on_ret(self, dbg, exception): + """Setup a breakpoint at the return address of the function, this breakpoint will call :func:`ret_trigger`""" + cproc = dbg.current_process + return_addr = dbg.current_process.read_ptr(dbg.current_thread.context.sp) + dbg.add_bp(FunctionRetBP(return_addr, self), target=dbg.current_process) + + def ret_trigger(self, dbg, exception): + """Called at the return of the function if :func:`break_on_ret` was called""" + raise NotImplementedError("ret_trigger") + + +
[docs]class FunctionBP(FunctionCallBP, FunctionParamDumpBP): + """A breakpoint that accept a function from :mod:`windows.winproxy` and able to: + + - Extract the arguments of the functions + - Break at the return of the function + """
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/debug/debugger.html b/docs/build/html/_modules/windows/debug/debugger.html new file mode 100644 index 0000000..7838fb3 --- /dev/null +++ b/docs/build/html/_modules/windows/debug/debugger.html @@ -0,0 +1,992 @@ + + + + + + + + windows.debug.debugger — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.debug.debugger

+import os.path
+from collections import defaultdict, namedtuple
+from contextlib import contextmanager
+
+import windows
+import windows.winobject.exception as winexception
+import windows.native_exec.simple_x86 as x86
+import windows.native_exec.simple_x64 as x64
+
+from windows.winobject.process import WinProcess, WinThread
+from windows.dbgprint import dbgprint
+from windows import winproxy
+from windows.generated_def.winstructs import *
+from windows.generated_def import windef
+from .breakpoints import *
+
+#from windows.syswow64 import CS_32bits
+from windows.winobject.exception import VectoredException
+
+
+PAGE_SIZE = 0x1000
+
+
+class DEBUG_EVENT(DEBUG_EVENT):
+    KNOWN_EVENT_CODE = dict((x,x) for x in [EXCEPTION_DEBUG_EVENT,
+        CREATE_THREAD_DEBUG_EVENT, CREATE_PROCESS_DEBUG_EVENT,
+        EXIT_THREAD_DEBUG_EVENT, EXIT_PROCESS_DEBUG_EVENT, LOAD_DLL_DEBUG_EVENT,
+        UNLOAD_DLL_DEBUG_EVENT, OUTPUT_DEBUG_STRING_EVENT, RIP_EVENT])
+
+    @property
+    def code(self):
+        return self.KNOWN_EVENT_CODE.get(self.dwDebugEventCode, self.dwDebugEventCode)
+
+WatchedPage = namedtuple('WatchedPage', ["original_prot", "bps"])
+
+
+
[docs]class Debugger(object): + """A debugger based on standard Win32 API. Handle : + + * Standard BP (int3) + * Hardware-Exec BP (DrX) + * Memory BP (virtual_protect)""" + +
[docs] def __init__(self, target): + """``target`` must be a debuggable :class:`WinProcess`.""" + self._init_dispatch_handlers() + self.target = target + self.is_target_launched = False + #if not already_debuggable: + # winproxy.DebugActiveProcess(target.pid) + self.processes = {} + self.threads = {} + self.current_process = None + self.current_thread = None + # List of breakpoints + self.breakpoints = {} + self._pending_breakpoints = {} #Breakpoints to put in new process / threads + # Values rewritten by "\xcc" + self._memory_save = defaultdict(dict) + # Dict of {tid : {drx taken : BP}} + self._hardware_breakpoint = {} + # Breakpoints to reput.. + self._breakpoint_to_reput = {} + + self._module_by_process = {} + + self._pending_breakpoints_new = defaultdict(list) + + self._explicit_single_step = {} + + self._watched_pages = {}# Dict [page_modif] -> [mem bp on the page] + + # [start] -> (size, current_proctection, original_prot) + self._virtual_protected_memory = [] # List of memory-range modified by a MemBP
+ + + @classmethod +
[docs] def attach(cls, target): + """attach to ``target`` (must be a :class:`WinProcess`) + + :rtype: :class:`Debugger`""" + winproxy.DebugActiveProcess(target.pid) + return cls(target)
+ + def detach(self, target=None): + if target is None: + for proc in self.processes.values(): + self.detach(proc) + return + if not isinstance(target, WinProcess): + raise ValueError("Detach accept only WinProcess") + + self.disable_all_memory_breakpoints(target) + for bp in self.breakpoints[target.pid].values(): + if not bp.apply_to_target(target): + target_threads = [t for t in target.threads if t.tid in self.threads] + bp_threads = [] + # TODO: clean API tu request HXBP on a thread + for t in target_threads: + t_bps = [pos for pos, hbp in self._hardware_breakpoint[t.tid].items() if hbp == bp] + if t_bps: + bp_threads.append(t) + self.del_bp(bp, bp_threads) + else: + self.del_bp(bp, [target]) + + for thread in [t for t in target.threads if t.tid in self.threads]: + del self._explicit_single_step[thread.tid] + del self._breakpoint_to_reput[thread.tid] + del self.threads[thread.tid] + del self.processes[target.pid] + del self._watched_pages[target.pid] + if target is self.current_process: + self._finish_debug_event(self.REMOVE_ME_debug_event, DBG_CONTINUE) + + windows.winproxy.DebugActiveProcessStop(target.pid) + + def _killed_in_action(self): + """Return True if current process have been detached by user callback""" + return self.current_process.pid not in self.processes + + + @classmethod +
[docs] def debug(cls, path, args=None, dwCreationFlags=0, show_windows=False): + """Create a process and debug it. + + :rtype: :class:`Debugger`""" + dwCreationFlags |= DEBUG_PROCESS + c = windows.utils.create_process(path, args=args, dwCreationFlags=dwCreationFlags, show_windows=show_windows) + return cls(c)
+ + def _init_dispatch_handlers(self): + dbg_evt_dispatch = {} + dbg_evt_dispatch[EXCEPTION_DEBUG_EVENT] = self._handle_exception + dbg_evt_dispatch[CREATE_THREAD_DEBUG_EVENT] = self._handle_create_thread + dbg_evt_dispatch[CREATE_PROCESS_DEBUG_EVENT] = self._handle_create_process + dbg_evt_dispatch[EXIT_PROCESS_DEBUG_EVENT] = self._handle_exit_process + dbg_evt_dispatch[EXIT_THREAD_DEBUG_EVENT] = self._handle_exit_thread + dbg_evt_dispatch[LOAD_DLL_DEBUG_EVENT] = self._handle_load_dll + dbg_evt_dispatch[UNLOAD_DLL_DEBUG_EVENT] = self._handle_unload_dll + dbg_evt_dispatch[RIP_EVENT] = self._handle_rip + dbg_evt_dispatch[OUTPUT_DEBUG_STRING_EVENT] = self._handle_output_debug_string + self._DebugEventCode_dispatch = dbg_evt_dispatch + + def _debug_event_generator(self): + while True: + debug_event = DEBUG_EVENT() + winproxy.WaitForDebugEvent(debug_event) + yield debug_event + + def _finish_debug_event(self, event, action): + if action not in [windef.DBG_CONTINUE, windef.DBG_EXCEPTION_NOT_HANDLED]: + raise ValueError('Unknow action : <0>'.format(action)) + winproxy.ContinueDebugEvent(event.dwProcessId, event.dwThreadId, action) + + def _update_debugger_state(self, debug_event): + self.current_process = self.processes[debug_event.dwProcessId] + self.current_thread = self.threads[debug_event.dwThreadId] + + def _dispatch_debug_event(self, debug_event): + #print("DISPATCH {0}".format(DEBUG_EVENT.KNOWN_EVENT_CODE.get(debug_event.dwDebugEventCode))) + handler = self._DebugEventCode_dispatch.get(debug_event.dwDebugEventCode, self._handle_unknown_debug_event) + return handler(debug_event) + + def _dispatch_breakpoint(self, exception, addr): + bp = self.breakpoints[self.current_process.pid][addr] + with self.DisabledMemoryBreakpoint(): + x = bp.trigger(self, exception) + return x + + def _resolve(self, addr, target): + if not isinstance(addr, basestring): + return addr + dll, api = addr.split("!") + dll = dll.lower() + modules = self._module_by_process[target.pid] + mod = None + if dll in modules: + mod = [modules[dll]] + if not mod: + return None + # TODO: optim exports are the same for whole system (32 vs 64 bits) + # I don't have to reparse the exports each time.. + # Try to interpret api as an int + try: + api_int = int(api, 0) + return mod[0].baseaddr + api_int + except ValueError: + pass + exports = mod[0].exports + if api not in exports: + raise ValueError("Unknown API <{0}> in DLL {1}".format(api, dll)) + return exports[api] + + def add_pending_breakpoint(self, bp, target): + self._pending_breakpoints_new[target].append(bp) + + def remove_pending_breakpoint(self, bp, target): + self._pending_breakpoints_new[target].remove(bp) + + def _setup_breakpoint(self, bp, target): + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + if target is None: + if bp.type in [STANDARD_BP, MEMORY_BREAKPOINT]: #TODO: better.. + targets = self.processes.values() + else: + targets = self.threads.values() + else: + targets = [target] + for target in targets: + return _setup_method(bp, target) + + def _restore_breakpoints(self): + for bp in self._breakpoint_to_reput[self.current_thread.tid]: + if bp.type == HARDWARE_EXEC_BP: + raise NotImplementedError("Why is this here ? we use RF flags to pass HXBP") + restore = getattr(self, "_restore_breakpoint_" + bp.type) + restore(bp, self.current_process) + del self._breakpoint_to_reput[self.current_thread.tid][:] + return + + def _setup_breakpoint_BP(self, bp, target): + if not isinstance(target, WinProcess): + raise ValueError("SETUP STANDARD_BP on {0}".format(target)) + + addr = self._resolve(bp.addr, target) + if addr is None: + return False + bp._addr = addr + self._memory_save[target.pid][addr] = target.read_memory(addr, 1) + self.breakpoints[target.pid][addr] = bp + target.write_memory(addr, "\xcc") + return True + + def _restore_breakpoint_BP(self, bp, target): + self._memory_save[target.pid][bp._addr] = target.read_memory(bp._addr, 1) + return target.write_memory(bp._addr, "\xcc") + + def _remove_breakpoint_BP(self, bp, target): + if not isinstance(target, WinProcess): + raise ValueError("SETUP STANDARD_BP on {0}".format(target)) + addr = self._resolve(bp.addr, target) + target.write_memory(addr, self._memory_save[target.pid][addr]) + del self._memory_save[target.pid][addr] + del self.breakpoints[target.pid][addr] + return True + + def _setup_breakpoint_HXBP(self, bp, target): + #print("Setup {0} into {1}".format(bp, target)) + if not isinstance(target, WinThread): + raise ValueError("SETUP HXBP_BP on {0}".format(target)) + # Todo: opti, not reparse exports for all thread of the same process.. + addr = self._resolve(bp.addr, target.owner) + if addr is None: + return False + x = self._hardware_breakpoint[target.tid] + if all(pos in x for pos in range(4)): + raise ValueError("Cannot put {0} in {1} (DRx full)".format(bp, target)) + empty_drx = str([pos for pos in range(4) if pos not in x][0]) + ctx = target.context + ctx.EDr7.GE = 1 + ctx.EDr7.LE = 1 + setattr(ctx.EDr7, "L" + empty_drx, 1) + setattr(ctx, "Dr" + empty_drx, addr) + x[int(empty_drx)] = bp + target.set_context(ctx) + self.breakpoints[target.owner.pid][addr] = bp + return True + + def _remove_breakpoint_HXBP(self, bp, target): + if not isinstance(target, WinThread): + raise ValueError("SETUP HXBP_BP on {0}".format(target)) + addr = self._resolve(bp.addr, target.owner) + bp_pos = [pos for pos, hbp in self._hardware_breakpoint[target.tid].items() if hbp == bp] + if not bp_pos: + raise ValueError("Asked to remove {0} from {1} but not present in hbp_list".format(bp, target)) + bp_pos_str = str(bp_pos[0]) + ctx = target.context + setattr(ctx.EDr7, "L" + bp_pos_str, 0) + setattr(ctx, "Dr" + bp_pos_str, 0) + target.set_context(ctx) + try: # TODO: vraiment faire les HXBP par thread ? ... + del self.breakpoints[target.owner.pid][addr] + except: + pass + return True + + ## MemBP internal helpers + def _compute_page_access_for_event(self, target, events): + if "R" in events: + return PAGE_NOACCESS + if set("WX").issubset(events): + return PAGE_READONLY + if events == set("W"): + return PAGE_EXECUTE_READ + if events == set("X"): + # Might have problem if DEP is not enabled + if windows.winproxy.is_implemented(windows.winproxy.GetProcessDEPPolicy): + has_DEP = DWORD() + permaned = LONG() + windows.winproxy.GetProcessDEPPolicy(target.handle, has_DEP, permaned) + has_DEP = has_DEP.value + else: + has_DEP = 0 + return PAGE_READWRITE if has_DEP else PAGE_NOACCESS + raise ValueError("Unexpected set of event for Membp: {0}".format(events)) + + + def _setup_breakpoint_MEMBP(self, bp, target): + addr = self._resolve(bp.addr, target) + bp._addr = addr + self._events = set(bp.events) + if addr is None: + return False + # Split in affected pages: + protection_for_bp = self._compute_page_access_for_event(target, self._events) + affected_pages = range((addr >> 12) << 12, addr + bp.size, PAGE_SIZE) + old_prot = DWORD() + cp_watch_page = self._watched_pages[self.current_process.pid] + for page_addr in affected_pages: + if page_addr not in cp_watch_page: + target.virtual_protect(page_addr, PAGE_SIZE, protection_for_bp, old_prot) + # Page with no other MemBP + cp_watch_page[page_addr] = WatchedPage(old_prot.value, [bp]) + else: + # Reduce the right of the page to the common need + cp_watch_page[page_addr].bps.append(bp) + full_page_events = set.union(*[bp.events for bp in cp_watch_page[page_addr].bps]) + protection_for_page = self._compute_page_access_for_event(target, full_page_events) + target.virtual_protect(page_addr, PAGE_SIZE, protection_for_page, None) + # TODO: watch for overlap with other MEM breakpoints + return True + + def _restore_breakpoint_MEMBP(self, bp, target): + (page_addr, page_prot) = bp._reput_page + return target.virtual_protect(page_addr, PAGE_SIZE, page_prot, None) + + + def _remove_breakpoint_MEMBP(self, bp, target): + affected_pages = range((bp._addr >> 12) << 12, bp._addr + bp.size, PAGE_SIZE) + vprot_begin = affected_pages[0] + vprot_size = PAGE_SIZE * len(affected_pages) + cp_watch_page = self._watched_pages[self.current_process.pid] + for page_addr in affected_pages: + cp_watch_page[page_addr].bps.remove(bp) + if not cp_watch_page[page_addr].bps: + target.virtual_protect(page_addr, PAGE_SIZE, cp_watch_page[page_addr].original_prot, None) + del cp_watch_page[page_addr] + else: + full_page_events = set.union(*[bp.events for bp in cp_watch_page[page_addr].bps]) + protection_for_page = self._compute_page_access_for_event(target, full_page_events) + target.virtual_protect(page_addr, PAGE_SIZE, protection_for_page, None) + return True + + + def _setup_pending_breakpoints_new_process(self, new_process): + for bp in self._pending_breakpoints_new[None]: + if bp.apply_to_target(new_process): #BP for thread or process ? + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + _setup_method(bp, new_process) + + for bp in list(self._pending_breakpoints_new[new_process.pid]): + if bp.apply_to_target(new_process): + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + if _setup_method(bp, new_process): + self._pending_breakpoints_new[new_process.pid].remove(bp) + + def _setup_pending_breakpoints_new_thread(self, new_thread): + for bp in self._pending_breakpoints_new[None]: + if bp.apply_to_target(new_thread): #BP for thread or process ? + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + _setup_method(bp, new_thread) + + for bp in self._pending_breakpoints_new[new_thread.owner.pid]: + if bp.apply_to_target(new_thread): + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + _setup_method(bp, new_thread) + + for bp in list(self._pending_breakpoints_new[new_thread.tid]): + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + if _setup_method(bp, new_thread): + self._pending_breakpoints_new[new_thread.tid].remove(bp) + + + def _setup_pending_breakpoints_load_dll(self, dll_name): + for bp in self._pending_breakpoints_new[None]: + if isinstance(bp.addr, basestring): + target_dll = bp.addr.lower().split("!")[0] + if target_dll == dll_name: + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + if bp.apply_to_target(self.current_process): + _setup_method(bp, self.current_process) + else: + for t in [t for t in self.current_process.threads if t.tid in self.threads]: + _setup_method(bp, t) + + for bp in self._pending_breakpoints_new[self.current_process.pid]: + if isinstance(bp.addr, basestring): + target_dll = bp.addr.split("!")[0] + if target_dll == dll_name: + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + _setup_method(bp, self.current_process) + + for thread in self.current_process.threads: + for bp in self._pending_breakpoints_new[thread.tid]: + if isinstance(bp.addr, basestring): + target_dll = bp.addr.split("!")[0] + if target_dll == dll_name: + _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) + _setup_method(bp, self.thread) + + def _pass_breakpoint(self, addr): + process = self.current_process + thread = self.current_thread + process.write_memory(addr, self._memory_save[process.pid][addr]) + regs = thread.context + regs.EFlags |= (1 << 8) + #regs.pc -= 1 # Done in _handle_exception_breakpoint before dispatch + thread.set_context(regs) + bp = self.breakpoints[self.current_process.pid][addr] + self._breakpoint_to_reput[thread.tid].append(bp) #Register pending breakpoint for next single step + + def _pass_memory_breakpoint(self, bp, page_protect, fault_page): + cp = self.current_process + page_prot = DWORD() + cp.virtual_protect(fault_page, PAGE_SIZE, page_protect, page_prot) + thread = self.current_thread + ctx = thread.context + ctx.EEFlags.TF = 1 + thread.set_context(ctx) + bp._reput_page = (fault_page, page_prot.value) + self._breakpoint_to_reput[thread.tid].append(bp) + + # debug event handlers + def _handle_unknown_debug_event(self, debug_event): + raise NotImplementedError("dwDebugEventCode = {0}".format(debug_event.dwDebugEventCode)) + + + def _handle_exception_breakpoint(self, exception, excp_addr): + excp_bitness = self.get_exception_bitness(exception) + if excp_addr in self.breakpoints[self.current_process.pid]: + thread = self.current_thread + if self.current_process.bitness == 32 and excp_bitness == 64: + ctx = thread.context_syswow + else: + ctx = thread.context + ctx.pc -= 1 + if self.current_process.bitness == 32 and excp_bitness == 64: + thread.set_syswow_context(ctx) + else: + thread.set_context(ctx) + continue_flag = self._dispatch_breakpoint(exception, excp_addr) + if self._killed_in_action(): + return continue_flag + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + if excp_addr in self.breakpoints[self.current_process.pid]: + # Setup BP if not suppressed + self._pass_breakpoint(excp_addr) + return continue_flag + with self.DisabledMemoryBreakpoint(): + return self.on_exception(exception) + + def _handle_exception_singlestep(self, exception, excp_addr): + if self.current_thread.tid in self._breakpoint_to_reput and self._breakpoint_to_reput[self.current_thread.tid]: + self._restore_breakpoints() + if self._explicit_single_step[self.current_thread.tid]: + with self.DisabledMemoryBreakpoint(): + self.on_single_step(exception) + if not self._killed_in_action(): + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + return DBG_CONTINUE + elif excp_addr in self.breakpoints[self.current_process.pid]: + # Verif that's not a standard BP ? + bp = self.breakpoints[self.current_process.pid][excp_addr] + with self.DisabledMemoryBreakpoint(): + bp.trigger(self, exception) + if self._killed_in_action(): + return DBG_CONTINUE + ctx = self.current_thread.context + self._explicit_single_step[self.current_thread.tid] = ctx.EEFlags.TF + if excp_addr in self.breakpoints[self.current_process.pid]: + ctx.EEFlags.RF = 1 + self.current_thread.set_context(ctx) + return DBG_CONTINUE + elif self._explicit_single_step[self.current_thread.tid]: + with self.DisabledMemoryBreakpoint(): + continue_flag = self.on_single_step(exception) + if self._killed_in_action(): + return continue_flag + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + return continue_flag + else: + with self.DisabledMemoryBreakpoint(): + continue_flag = self.on_exception(exception) + if self._killed_in_action(): + return continue_flag + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + return continue_flag + + # === Testing PAGE_NOACCESS(0x1L) === + # exception: access violation reading 0x00470000 + # exception: access violation writing 0x00470000 + # === Testing PAGE_READONLY(0x2L) === + # exception: access violation writing 0x00470000 + # === Testing PAGE_READWRITE(0x4L) === + # === Testing PAGE_EXECUTE(0x10L) === + # exception: access violation writing 0x00470000 + # === Testing PAGE_EXECUTE_READ(0x20L) === + # exception: access violation writing 0x00470000 + # === Testing PAGE_EXECUTE_READWRITE(0x40L) === + + def _handle_exception_access_violation(self, exception, excp_addr): + READ = 0 + WRITE = 1 + EXEC = 2 + EVENT_STR = "RWX" + + fault_type = exception.ExceptionRecord.ExceptionInformation[0] + fault_addr = exception.ExceptionRecord.ExceptionInformation[1] + pc_addr = self.current_thread.context.pc + if fault_addr == pc_addr: + fault_type = EXEC + event = EVENT_STR[fault_type] + + fault_page = (fault_addr >> 12) << 12 + cp_watch_page = self._watched_pages[self.current_process.pid] + + mem_bp = self.get_memory_breakpoint_at(fault_addr, self.current_process) + if mem_bp is False: # No BP on this page + with self.DisabledMemoryBreakpoint(): + return self.on_exception(exception) + original_prot = cp_watch_page[fault_page].original_prot + if mem_bp is None or event not in mem_bp.events: # Page has MEMBP but None handle this address | event not asked by membp + # This hack is bad, find a BP on the page to restore original access.. + bp = cp_watch_page[fault_page].bps[-1] + self._pass_memory_breakpoint(bp, original_prot, fault_page) + return DBG_CONTINUE + + with self.DisabledMemoryBreakpoint(): + continue_flag = mem_bp.trigger(self, exception) + if self._killed_in_action(): + return continue_flag + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + # If BP has not been removed in trigger, pas it + if fault_page in cp_watch_page and mem_bp in cp_watch_page[fault_page].bps: + self._pass_memory_breakpoint(mem_bp, original_prot, fault_page) + return continue_flag + + + # TODO: self._explicit_single_step setup by single_step() ? check at the end ? finally ? + def _handle_exception(self, debug_event): + """Handle EXCEPTION_DEBUG_EVENT""" + exception = debug_event.u.Exception + self._update_debugger_state(debug_event) + + if windows.current_process.bitness == 32: + exception.__class__ = winexception.EEXCEPTION_DEBUG_INFO32 + else: + exception.__class__ = winexception.EEXCEPTION_DEBUG_INFO64 + + excp_code = exception.ExceptionRecord.ExceptionCode + excp_addr = exception.ExceptionRecord.ExceptionAddress + if excp_code in [EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT] and excp_addr in self.breakpoints[self.current_process.pid]: + return self._handle_exception_breakpoint(exception, excp_addr) + elif excp_code in [EXCEPTION_SINGLE_STEP, STATUS_WX86_SINGLE_STEP]: + return self._handle_exception_singlestep(exception, excp_addr) + elif excp_code == EXCEPTION_ACCESS_VIOLATION: + return self._handle_exception_access_violation(exception, excp_addr) + else: + with self.DisabledMemoryBreakpoint(): + continue_flag = self.on_exception(exception) + if self._killed_in_action(): + return continue_flag + self._explicit_single_step[self.current_thread.tid] = self.current_thread.context.EEFlags.TF + return continue_flag + + + def _get_loaded_dll(self, load_dll): + name_sufix = "" + pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) + if self.current_process.bitness == 32 and pe.bitness == 64: + name_sufix = "64" + + if not load_dll.lpImageName: + return pe.export_name + name_sufix + try: + addr = self.current_process.read_ptr(load_dll.lpImageName) + except: + addr = None + + if not addr: + pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) + dll_name = pe.export_name + if not dll_name: + dll_name = os.path.basename(self.current_process.get_mapped_filename(load_dll.lpBaseOfDll)) + return dll_name + name_sufix + + if load_dll.fUnicode: + return self.current_process.read_wstring(addr) + name_sufix + return self.current_process.read_string(addr) + name_sufix + + def _handle_create_process(self, debug_event): + """Handle CREATE_PROCESS_DEBUG_EVENT""" + create_process = debug_event.u.CreateProcessInfo + # Duplicate handle, so garbage collection of the process/thread does not + # break the debug API invariant (those x_event handle are close by the debug API itself) + proc_handle = HANDLE() + thread_handle = HANDLE() + cp_handle = windows.current_process.handle + winproxy.DuplicateHandle(cp_handle, create_process.hProcess, cp_handle, ctypes.byref(proc_handle), dwOptions=DUPLICATE_SAME_ACCESS) + winproxy.DuplicateHandle(cp_handle, create_process.hThread, cp_handle, ctypes.byref(thread_handle), dwOptions=DUPLICATE_SAME_ACCESS) + + self.current_process = WinProcess._from_handle(proc_handle.value) + self.current_thread = WinThread._from_handle(thread_handle.value) + + self.threads[self.current_thread.tid] = self.current_thread + self._explicit_single_step[self.current_thread.tid] = False + self._hardware_breakpoint[self.current_thread.tid] = {} + self._breakpoint_to_reput[self.current_thread.tid] = [] + self.processes[self.current_process.pid] = self.current_process + self._watched_pages[self.current_process.pid] = {} #defaultdict(list) + self.breakpoints[self.current_process.pid] = {} + self._module_by_process[self.current_process.pid] = {} + self._update_debugger_state(debug_event) + self._setup_pending_breakpoints_new_process(self.current_process) + self._setup_pending_breakpoints_new_thread(self.current_thread) + with self.DisabledMemoryBreakpoint(): + return self.on_create_process(create_process) + # TODO: close hFile + + def _handle_exit_process(self, debug_event): + """Handle EXIT_PROCESS_DEBUG_EVENT""" + self._update_debugger_state(debug_event) + exit_process = debug_event.u.ExitProcess + retvalue = self.on_exit_process(exit_process) + del self.threads[self.current_thread.tid] + del self._explicit_single_step[self.current_thread.tid] + del self._hardware_breakpoint[self.current_thread.tid] + del self._breakpoint_to_reput[self.current_thread.tid] + del self.processes[self.current_process.pid] + del self._watched_pages[self.current_process.pid] + return retvalue + + def _handle_create_thread(self, debug_event): + """Handle CREATE_THREAD_DEBUG_EVENT""" + create_thread = debug_event.u.CreateThread + # Duplicate handle, so garbage collection of the thread does not + # break the debug API invariant (those x_event handle are close by the debug API itself) + thread_handle = HANDLE() + cp_handle = windows.current_process.handle + winproxy.DuplicateHandle(cp_handle, create_thread.hThread, cp_handle, ctypes.byref(thread_handle), dwOptions=DUPLICATE_SAME_ACCESS) + self.current_thread = WinThread._from_handle(thread_handle.value) + self.threads[self.current_thread.tid] = self.current_thread + self._explicit_single_step[self.current_thread.tid] = False + self._breakpoint_to_reput[self.current_thread.tid] = [] + self._hardware_breakpoint[self.current_thread.tid] = {} + self._setup_pending_breakpoints_new_thread(self.current_thread) + with self.DisabledMemoryBreakpoint(): + return self.on_create_thread(create_thread) + + + def _handle_exit_thread(self, debug_event): + """Handle EXIT_THREAD_DEBUG_EVENT""" + self._update_debugger_state(debug_event) + exit_thread = debug_event.u.ExitThread + with self.DisabledMemoryBreakpoint(): + retvalue = self.on_exit_thread(exit_thread) + del self.threads[self.current_thread.tid] + del self._hardware_breakpoint[self.current_thread.tid] + del self._explicit_single_step[self.current_thread.tid] + del self._breakpoint_to_reput[self.current_thread.tid] + return retvalue + + def _handle_load_dll(self, debug_event): + """Handle LOAD_DLL_DEBUG_EVENT""" + self._update_debugger_state(debug_event) + load_dll = debug_event.u.LoadDll + dll = self._get_loaded_dll(load_dll) + dll_name = os.path.basename(dll).lower() + if dll_name.endswith(".dll"): + dll_name = dll_name[:-4] + if dll_name.endswith(".dll64"): + dll_name = dll_name[:-6] + "64" # Crade.. + #print("Load {0} -> {1}".format(dll, dll_name)) + self._module_by_process[self.current_process.pid][dll_name] = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process) + self._setup_pending_breakpoints_load_dll(dll_name) + with self.DisabledMemoryBreakpoint(): + return self.on_load_dll(load_dll) + + def _handle_unload_dll(self, debug_event): + """Handle UNLOAD_DLL_DEBUG_EVENT""" + self._update_debugger_state(debug_event) + unload_dll = debug_event.u.UnloadDll + with self.DisabledMemoryBreakpoint(): + return self.on_unload_dll(unload_dll) + + def _handle_output_debug_string(self, debug_event): + """Handle OUTPUT_DEBUG_STRING_EVENT""" + self._update_debugger_state(debug_event) + debug_string = debug_event.u.DebugString + with self.DisabledMemoryBreakpoint(): + return self.on_output_debug_string(debug_string) + + def _handle_rip(self, debug_event): + """Handle RIP_EVENT""" + self._update_debugger_state(debug_event) + rip_info = debug_event.u.RipInfo + with self.DisabledMemoryBreakpoint(): + return self.on_rip(rip_info) + + ## Public API +
[docs] def loop(self): + """Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead""" + for debug_event in self._debug_event_generator(): + self.REMOVE_ME_debug_event = debug_event + dbg_continue_flag = self._dispatch_debug_event(debug_event) + if dbg_continue_flag is None: + dbg_continue_flag = DBG_CONTINUE + if not self._killed_in_action(): + self._finish_debug_event(debug_event, dbg_continue_flag) + if not self.processes: + break
+ +
[docs] def add_bp(self, bp, addr=None, type=None, target=None): + """Add a breakpoint, bp can be: + + * a :class:`Breakpoint` (addr and type must be ``None``) + * any callable (addr and type must NOT be ``None``) (NON-TESTED) + + If the ``bp`` type is ``STANDARD_BP`` or ``MEMORY_BREAKPOINT``, target can be ``None`` (all targets) or a process. + + If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be ``None`` (all targets), a process or a thread. + """ + if getattr(bp, "addr", None) is None: + if addr is None or type is None: + raise ValueError("SUCK YOUR NONE") + bp = ProxyBreakpoint(bp, addr, type) + else: + if addr is not None or type is not None: + raise ValueError("Given <addr|type> by parameters but BP object have them") + del addr + del type + + if target is None: + # Need to add it to all other breakpoint + self.add_pending_breakpoint(bp, None) + elif target is not None: + # Check that targets are accepted + if target not in self.processes.values() + self.threads.values(): + if target == self.target: # Original target (that have not been lauched yet) + return self.add_pending_breakpoint(bp, target) + else: + raise ValueError("Unknown target {0}".format(target)) + return self._setup_breakpoint(bp, target)
+ +
[docs] def del_bp(self, bp, targets=None): + """Delete a breakpoint, if targets is ``None``: delete it from all targets""" + original_target = targets + _remove_method = getattr(self, "_remove_breakpoint_" + bp.type) + if targets is None: + if bp.type in [STANDARD_BP, MEMORY_BREAKPOINT]: #TODO: better.. + targets = self.processes.values() + else: + targets = self.threads.values() + for target in targets: + _remove_method(bp, target) + if original_target is None: + return self.remove_pending_breakpoint(bp, original_target)
+ +
[docs] def single_step(self): + """Make the ``current_thread`` ``single_step``. ``Debugger.on_single_step`` will be called after that""" + t = self.current_thread + ctx = t.context + ctx.EEFlags.TF = 1 + t.set_context(ctx)
+ + ## Memory Breakpoint helper +
[docs] def get_memory_breakpoint_at(self, addr, process=None): + """Get the memory breakpoint that handle ``addr`` + + Return values are: + + * ``False`` if the page has no memory breakpoint (real fault) + * ``None`` if the page as memBP but None handle ``addr`` + * ``bp`` the MemBP that handle ``addr`` + """ + if process is None: + process = self.current_process + + fault_page = (addr >> 12) << 12 + if fault_page not in self._watched_pages[process.pid]: + return False + + for bp in self._watched_pages[process.pid][fault_page].bps: + if bp._addr <= addr < bp._addr + bp.size: + return bp + return None
+ +
[docs] def disable_all_memory_breakpoints(self, target=None): + """Restore all pages to their original access rights. + If target is ``None``, use ``current_process`` + + :return: a mapping of all disabled breakpoints that must be passed to :func:`restore_all_memory_breakpoints`""" + if target is None: + target = self.current_process + res = {} + cp_watch_page = self._watched_pages[self.current_process.pid] + page_protection = DWORD() + for page_addr, watched_page in cp_watch_page.items(): + target.virtual_protect(page_addr, PAGE_SIZE, watched_page.original_prot, page_protection) + res[page_addr] = page_protection.value + return res
+ + +
[docs] def restore_all_memory_breakpoints(self, data, target=None): + """Re-setup all memory breakpoints, affecting pages access rights. + If target is ``None``, use ``current_process`` + + ``data`` is the result of the corresponding call to :func:`disable_all_memory_breakpoints`""" + if target is None: + target = self.current_process + for page_addr, protection in data.items(): + # Prevent restoring deleted breakpoints + if page_addr in self._watched_pages[target.pid]: + target.virtual_protect(page_addr, PAGE_SIZE, protection, None) + return
+ + @contextmanager +
[docs] def DisabledMemoryBreakpoint(self, target=None): + """A context-manager that disable all memory breakpoints and restore them on exit""" + data = self.disable_all_memory_breakpoints(target) + try: + yield + finally: + if not self._killed_in_action(): + self.restore_all_memory_breakpoints(data, target)
+ +
[docs] def get_exception_bitness(self, exc): + """Return the bitness in which the exception occured. + Useful when debugingg a 32b process from a 64bits one + + :return: :class:`int` -- 32 or 64""" + if windows.current_process.bitness == 32: + return 32 + if exc.ExceptionRecord.ExceptionCode in [STATUS_WX86_BREAKPOINT, STATUS_WX86_SINGLE_STEP]: + return 32 + return 64
+ + # Public callback +
[docs] def on_exception(self, exception): + """Called on exception event other that known breakpoint or requested single step. ``exception`` is one of the following type: + + * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32` + * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64` + + The default behaviour is to return ``DBG_CONTINUE`` for the known exception code + and ``DBG_EXCEPTION_NOT_HANDLED`` else + """ + if not exception.ExceptionRecord.ExceptionCode in winexception.exception_name_by_value: + return DBG_EXCEPTION_NOT_HANDLED + return DBG_CONTINUE
+ +
[docs] def on_single_step(self, exception): + """Called on requested single step``exception`` is one of the following type: + + * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32` + * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64` + + There is no default implementation, if you use ``Debugger.single_step()`` you should implement ``on_single_step`` + """ + raise NotImplementedError("Debugger that explicitly single step should implement <on_single_step>")
+ +
[docs] def on_create_process(self, create_process): + """Called on create_process event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679286(v=vs.85).aspx)""" + pass
+ +
[docs] def on_exit_process(self, exit_process): + """Called on exit_process event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679334(v=vs.85).aspx)""" + pass
+ +
[docs] def on_create_thread(self, create_thread): + """Called on create_thread event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679287(v=vs.85).aspx)""" + pass
+ +
[docs] def on_exit_thread(self, exit_thread): + """Called on exit_thread event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms679335(v=vs.85).aspx)""" + pass
+ +
[docs] def on_load_dll(self, load_dll): + """Called on load_dll event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680351(v=vs.85).aspx)""" + pass
+ +
[docs] def on_unload_dll(self, unload_dll): + """Called on unload_dll event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681403(v=vs.85).aspx)""" + pass
+ +
[docs] def on_output_debug_string(self, debug_string): + """Called on debug_string event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680545(v=vs.85).aspx)""" + pass
+ +
[docs] def on_rip(self, rip_info): + """Called on rip_info event (for param type see https://msdn.microsoft.com/en-us/library/windows/desktop/ms680587(v=vs.85).aspx)""" + pass
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/debug/localdbg.html b/docs/build/html/_modules/windows/debug/localdbg.html new file mode 100644 index 0000000..f3a7113 --- /dev/null +++ b/docs/build/html/_modules/windows/debug/localdbg.html @@ -0,0 +1,345 @@ + + + + + + + + windows.debug.localdbg — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.debug.localdbg

+from collections import defaultdict
+from contextlib import contextmanager
+
+import windows
+import windows.winobject.exception as winexception
+
+from windows import winproxy
+from windows.generated_def import windef
+from windows.generated_def.winstructs import *
+from .breakpoints import *
+
+
+
[docs]class LocalDebugger(object): + """A debugger interface around :func:`AddVectoredExceptionHandler`. + + Handle: + + * Standard BP (int3) + * Hardware-Exec BP (DrX)""" + + def __init__(self): + self.breakpoints = {} + self._memory_save = {} + self._reput_breakpoint = {} + self._hxbp_breakpoint = defaultdict(dict) + + self.callback_vectored = winexception.VectoredException(self.callback) + winproxy.AddVectoredExceptionHandler(0, self.callback_vectored) + self.setup_hxbp_callback_vectored = winexception.VectoredException(self.setup_hxbp_callback) + self.hxbp_info = None + self.code = windows.native_exec.create_function("\xcc\xc3", [PVOID]) + self.veh_depth = 0 + self.current_exception = None + self.exceptions_stack = [None] + + @contextmanager + def NewCurrentException(self, exc): + try: + self.exceptions_stack.append(exc) + self.current_exception = exc + self.veh_depth += 1 + yield exc + finally: + self.exceptions_stack.pop() + self.current_exception = self.exceptions_stack[-1] + self.veh_depth -= 1 + +
[docs] def get_exception_code(self): + """Return ExceptionCode of current exception""" + return self.current_exception[0].ExceptionRecord[0].ExceptionCode
+ +
[docs] def get_exception_context(self): + """Return context of current exception""" + return self.current_exception[0].ContextRecord[0]
+ +
[docs] def single_step(self): + """Make the current thread to single step""" + self.get_exception_context().EEFlags.TF = 1 + return windef.EXCEPTION_CONTINUE_EXECUTION
+ + def _pass_breakpoint(self, addr, single_step): + with windows.utils.VirtualProtected(addr, 1, PAGE_EXECUTE_READWRITE): + windows.current_process.write_memory(addr, self._memory_save[addr]) + self._reput_breakpoint[windows.current_thread.tid] = self.breakpoints[addr], single_step + return self.single_step() + + def callback(self, exc): + with self.NewCurrentException(exc): + return self.handle_exception(exc) + + def handle_exception(self, exc): + exp_code = self.get_exception_code() + context = self.get_exception_context() + exp_addr = context.pc + + if exp_code == EXCEPTION_BREAKPOINT and exp_addr in self.breakpoints: + res = self.breakpoints[exp_addr].trigger(self, exc) + single_step = self.get_exception_context().EEFlags.TF # single step activated by breakpoint + if exp_addr in self.breakpoints: # Breakpoint deleted itself ? + return self._pass_breakpoint(exp_addr, single_step) + return EXCEPTION_CONTINUE_EXECUTION + + if exp_code == EXCEPTION_SINGLE_STEP and windows.current_thread.tid in self._reput_breakpoint: + bp, single_step = self._reput_breakpoint[windows.current_thread.tid] + self._memory_save[bp.addr] = windows.current_process.read_memory(bp.addr, 1) + with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE): + windows.current_process.write_memory(bp.addr, "\xcc") + del self._reput_breakpoint[windows.current_thread.tid] + if single_step: + return self.on_exception(exc) + return windef.EXCEPTION_CONTINUE_EXECUTION + elif exp_code == EXCEPTION_SINGLE_STEP and exp_addr in self._hxbp_breakpoint[windows.current_thread.tid]: + res = self._hxbp_breakpoint[windows.current_thread.tid][exp_addr].trigger(self, exc) + context.EEFlags.RF = 1 + return EXCEPTION_CONTINUE_EXECUTION + return self.on_exception(exc) + +
[docs] def on_exception(self, exc): + """Called on exception""" + if not self.get_exception_code() in winexception.exception_name_by_value: + return windef.EXCEPTION_CONTINUE_SEARCH + return windef.EXCEPTION_CONTINUE_EXECUTION
+ +
[docs] def del_bp(self, bp): + """Delete a breakpoint""" + if bp.type == STANDARD_BP: + with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE): + windows.current_process.write_memory(bp.addr, self._memory_save[bp.addr]) + del self._memory_save[bp.addr] + del self.breakpoints[bp.addr] + return + if bp.type == HARDWARE_EXEC_BP: + threads_by_tid = {t.tid: t for t in windows.current_process.threads} + for tid in self._hxbp_breakpoint: + if bp.addr in self._hxbp_breakpoint[tid] and self._hxbp_breakpoint[tid][bp.addr] == bp: + if tid == windows.current_thread.tid: + self.remove_hxbp_self_thread(bp.addr) + else: + self.remove_hxbp_other_thread(bp.addr, threads_by_tid[tid]) + del self._hxbp_breakpoint[tid][bp.addr] + return + raise NotImplementedError("Unknow BP type {0}".format(bp.type))
+ +
[docs] def add_bp(self, bp, targets=None): + """Add a breakpoint, bp is a "class:`Breakpoint` + + If the ``bp`` type is ``STANDARD_BP``, target must be None. + + If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be None (all threads), or some threads of the process + """ + if bp.type == HARDWARE_EXEC_BP: + return self.add_bp_hxbp(bp, targets) + if bp.type != STANDARD_BP: + raise NotImplementedError("Unknow BP type {0}".format(bp.type)) + if targets is not None: + raise ValueError("LocalDebugger: STANDARD_BP doest not support targets {0}".format(targets)) + self.breakpoints[bp.addr] = bp + self._memory_save[bp.addr] = windows.current_process.read_memory(bp.addr, 1) + with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE): + windows.current_process.write_memory(bp.addr, "\xcc") + return
+ + def add_bp_hxbp(self, bp, targets=None): + if bp.type != HARDWARE_EXEC_BP: + raise NotImplementedError("Add non standard-BP in LocalDebugger") + if targets is None: + targets = windows.current_process.threads + for thread in targets: + if thread.owner.pid != windows.current_process.pid: + raise ValueError("Cannot add HXBP to target in remote process {0}".format(thread)) + if thread.tid == windows.current_thread.tid: + self.setup_hxbp_self_thread(bp.addr) + else: + self.setup_hxbp_other_thread(bp.addr, thread) + self._hxbp_breakpoint[thread.tid][bp.addr] = bp + + def setup_hxbp_callback(self, exc): + with self.NewCurrentException(exc): + exp_code = self.get_exception_code() + context = self.get_exception_context() + exp_addr = context.pc + hxbp_used = self.setup_hxbp_in_context(context, self.data) + windows.current_process.write_memory(exp_addr, "\x90") + # Raising in the VEH is a bad idea.. + # So better give the information to triggerer.. + if hxbp_used is not None: + self.get_exception_context().Eax = exp_addr + else: + self.get_exception_context().Eax = 0 + return windef.EXCEPTION_CONTINUE_EXECUTION + + def remove_hxbp_callback(self, exc): + with self.NewCurrentException(exc): + exp_code = self.get_exception_code() + context = self.get_exception_context() + exp_addr = context.pc + hxbp_used = self.remove_hxbp_in_context(context, self.data) + windows.current_process.write_memory(exp_addr, "\x90") + # Raising in the VEH is a bad idea.. + # So better give the information to triggerer.. + if hxbp_used is not None: + self.get_exception_context().Eax = exp_addr + else: + self.get_exception_context().Eax = 0 + return windef.EXCEPTION_CONTINUE_EXECUTION + + def setup_hxbp_in_context(self, context, addr): + for i in range(4): + is_used = getattr(context.EDr7, "L" + str(i)) + empty_drx = str(i) + if not is_used: + context.EDr7.GE = 1 + context.EDr7.LE = 1 + setattr(context.EDr7, "L" + empty_drx, 1) + setattr(context, "Dr" + empty_drx, addr) + return i + return None + + def remove_hxbp_in_context(self, context, addr): + for i in range(4): + target_drx = str(i) + is_used = getattr(context.EDr7, "L" + str(i)) + draddr = getattr(context, "Dr" + target_drx) + + if is_used and draddr == addr: + setattr(context.EDr7, "L" + target_drx, 0) + setattr(context, "Dr" + target_drx, 0) + return i + return None + + def setup_hxbp_self_thread(self, addr): + if self.current_exception is not None: + x = self.setup_hxbp_in_context(self.get_exception_context(), addr) + if x is None: + raise ValueError("Could not setup HXBP") + return + + self.data = addr + with winexception.VectoredExceptionHandler(1, self.setup_hxbp_callback): + x = self.code() + if x is None: + raise ValueError("Could not setup HXBP") + windows.current_process.write_memory(x, "\xcc") + return + + def setup_hxbp_other_thread(self, addr, thread): + thread.suspend() + ctx = thread.context + x = self.setup_hxbp_in_context(ctx, addr) + if x is None: + raise ValueError("Could not setup HXBP in {0}".format(thread)) + thread.set_context(ctx) + thread.resume() + + def remove_hxbp_self_thread(self, addr): + if self.current_exception is not None: + x = self.remove_hxbp_in_context(self.get_exception_context(), addr) + if x is None: + raise ValueError("Could not setup HXBP") + return + self.data = addr + with winexception.VectoredExceptionHandler(1, self.remove_hxbp_callback): + x = self.code() + if x is None: + raise ValueError("Could not remove HXBP") + windows.current_process.write_memory(x, "\xcc") + return + + def remove_hxbp_other_thread(self, addr, thread): + thread.suspend() + ctx = thread.context + x = self.remove_hxbp_in_context(ctx, addr) + if x is None: + raise ValueError("Could not setup HXBP in {0}".format(thread)) + thread.set_context(ctx) + thread.resume()
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/hooks.html b/docs/build/html/_modules/windows/hooks.html new file mode 100644 index 0000000..156e8a9 --- /dev/null +++ b/docs/build/html/_modules/windows/hooks.html @@ -0,0 +1,191 @@ + + + + + + + + windows.hooks — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.hooks

+import sys
+import ctypes
+
+import windows.utils as utils
+from . import native_exec
+from .generated_def import winfuncs
+from .generated_def.windef import PAGE_EXECUTE_READWRITE
+from .generated_def.winstructs import *
+
+
+
[docs]class Callback(object): + """Give type information to hook callback""" + def __init__(self, *types): + self.types = types + + def __call__(self, func): + func._types_info = self.types + return func
+ + +class KnownCallback(object): + types = () + + def __call__(self, func): + func._types_info = self.types + return func + + +def add_callback_to_module(callback): + setattr(sys.modules[__name__], type(callback).__name__, callback) + +# Generate IATCallback decorator for all known functions +for func in winfuncs.functions: + prototype = getattr(winfuncs, func + "Prototype") + callback_name = func + "Callback" + + class CallBackDeclaration(KnownCallback): + types = (prototype._restype_,) + prototype._argtypes_ + + CallBackDeclaration.__name__ = callback_name + add_callback_to_module(CallBackDeclaration()) + + +
[docs]class IATHook(object): + """Look at my hook <3""" + yolo = [] + + def __init__(self, IAT_entry, callback, types=None): + if types is None: + if not hasattr(callback, "_types_info"): + raise ValueError("Callback for IATHook has no type infomations") + types = callback._types_info + self.original_types = types + self.callback_types = self.transform_arguments(self.original_types) + self.entry = IAT_entry + self.callback = callback + self.stub = ctypes.WINFUNCTYPE(*self.callback_types)(self.hook_callback) + self.stub_addr = ctypes.cast(self.stub, PVOID).value + self.realfunction = ctypes.WINFUNCTYPE(*types)(IAT_entry.nonhookvalue) + self.is_enable = False + #IATHook.yolo.append(self) + + def transform_arguments(self, types): + res = [] + for type in types: + if type in (ctypes.c_wchar_p, ctypes.c_char_p): + res.append(ctypes.c_void_p) + else: + res.append(type) + return res + +
[docs] def enable(self): + """Enable the IAT hook: you MUST keep a reference to the IATHook while the hook is enabled""" + with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE): + self.entry.value = self.stub_addr + self.is_enable = True
+ +
[docs] def disable(self): + """Disable the IAT hook""" + with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE): + self.entry.value = self.entry.nonhookvalue + self.is_enable = False
+ + def hook_callback(self, *args): + adapted_args = [] + for value, type in zip(args, self.original_types[1:]): + if type == ctypes.c_wchar_p: + adapted_args.append(ctypes.c_wchar_p(value)) + elif type == ctypes.c_char_p: + adapted_args.append(ctypes.c_char_p((value))) + else: + adapted_args.append(value) + + def real_function(*args): + if args == (): + args = adapted_args + return self.realfunction(*args) + return self.callback(*adapted_args, real_function=real_function)
+ + # Use this tricks to prevent garbage collection of hook ? + #def __del__(self): + # pass +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/native_exec/cpuid.html b/docs/build/html/_modules/windows/native_exec/cpuid.html new file mode 100644 index 0000000..a57355e --- /dev/null +++ b/docs/build/html/_modules/windows/native_exec/cpuid.html @@ -0,0 +1,248 @@ + + + + + + + + windows.native_exec.cpuid — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.native_exec.cpuid

+import ctypes
+import struct
+
+import native_function
+import simple_x86 as x86
+import simple_x64 as x64
+from windows.generated_def.winstructs import *
+
+
+def _bitness():
+    """Returns 32 or 64"""
+    import platform
+    bits = platform.architecture()[0]
+    return int(bits[:2])
+
+
+
[docs]class X86CpuidResult(ctypes.Structure): + """Raw result of the CPUID instruction""" + _fields_ = [("EAX", DWORD), + ("EBX", DWORD), + ("ECX", DWORD), + ("EDX", DWORD)] + fields = [f[0] for f in _fields_] + """Fields of the Structure"""
+ +class X64CpuidResult(ctypes.Structure): + _fields_ = [("RAX", ULONG64), + ("RBX", ULONG64), + ("RCX", ULONG64), + ("RDX", ULONG64)] + + +class X86IntelCpuidFamilly(ctypes.Structure): + _fields_ = [("SteppingID", DWORD, 4), + ("ModelID", DWORD, 4), + ("FamilyID", DWORD, 4), + ("ProcessorType", DWORD, 2), + ("Reserved2", DWORD, 2), + ("ExtendedModel", DWORD, 4), + ("ExtendedFamily", DWORD, 8), + ("Reserved", DWORD, 2)] + fields = [f[0] for f in _fields_] + """Fields of the Structure""" + + +class X86AmdCpuidFamilly(ctypes.Structure): + _fields_ = [("SteppingID", DWORD, 4), + ("ModelID", DWORD, 4), + ("FamilyID", DWORD, 4), + ("Reserved2", DWORD, 4), + ("ExtendedModel", DWORD, 4), + ("ExtendedFamily", DWORD, 8), + ("Reserved", DWORD, 2)] + fields = [f[0] for f in _fields_] + """Fields of the Structure""" + +cpuid32_code = x86.MultipleInstr() +cpuid32_code += x86.Push('EDI') +cpuid32_code += x86.Mov('EAX', x86.mem('[ESP + 0x8]')) +cpuid32_code += x86.Mov('EDI', x86.mem('[ESP + 0xc]')) +cpuid32_code += x86.Cpuid() +cpuid32_code += x86.Mov(x86.mem('[EDI + 0x0]'), 'EAX') +cpuid32_code += x86.Mov(x86.mem('[EDI + 0x4]'), 'EBX') +cpuid32_code += x86.Mov(x86.mem('[EDI + 0x8]'), 'ECX') +cpuid32_code += x86.Mov(x86.mem('[EDI + 0xc]'), 'EDX') +cpuid32_code += x86.Pop('EDI') +cpuid32_code += x86.Ret() +do_cpuid32 = native_function.create_function(cpuid32_code.get_code(), [DWORD, DWORD, PVOID]) + + +cpuid64_code = x64.MultipleInstr() +cpuid64_code += x64.Mov('RAX', 'RCX') +cpuid64_code += x64.Mov('R10', 'RDX') +cpuid64_code += x64.Cpuid() +# For now assembler cannot do 32bits register in x64 +cpuid64_code += x64.Mov(x64.mem('[R10 + 0x00]'), 'RAX') +cpuid64_code += x64.Mov(x64.mem('[R10 + 0x08]'), 'RBX') +cpuid64_code += x64.Mov(x64.mem('[R10 + 0x10]'), 'RCX') +cpuid64_code += x64.Mov(x64.mem('[R10 + 0x18]'), 'RDX') +cpuid64_code += x64.Ret() +do_cpuid64 = native_function.create_function(cpuid64_code.get_code(), [DWORD, DWORD, PVOID]) + + +
[docs]def x86_cpuid(req): + """Performs a CPUID in 32bits mode + + :rtype: :class:`X86CpuidResult` + """ + cpuid_res = X86CpuidResult() + do_cpuid32(req, ctypes.addressof(cpuid_res)) + return cpuid_res
+ + +
[docs]def x64_cpuid(req): + """Performs a CPUID in 64bits mode + + :rtype: :class:`X86CpuidResult` + """ + cpuid_res = X64CpuidResult() + do_cpuid64(req, ctypes.addressof(cpuid_res)) + # For now assembler cannot do 32bits register in x64 + return X86CpuidResult(cpuid_res.RAX, cpuid_res.RBX, cpuid_res.RCX, cpuid_res.RDX)
+ + +if _bitness() == 32: + _do_cpuid = x86_cpuid +else: + _do_cpuid = x64_cpuid + +
[docs]def do_cpuid(req): + """Performs a CPUID for the current process bitness + + :rtype: :class:`X86CpuidResult` + """ + return _do_cpuid(req)
+ + +
[docs]def get_vendor_id(): + """Extracts the VendorId string from CPUID + + :rtype: :class:`str` + """ + cpuid_res = do_cpuid(0) + return struct.pack("<III", cpuid_res.EBX, cpuid_res.EDX, cpuid_res.ECX)
+ + +# platform.processor() could do the trick +
[docs]def is_intel_proc(): + """get_vendor_id() == 'GenuineIntel'""" + return get_vendor_id() == "GenuineIntel"
+ + +
[docs]def is_amd_proc(): + """get_vendor_id() == 'AuthenticAMD'""" + return get_vendor_id() == "AuthenticAMD"
+ + +
[docs]def get_proc_family_model(): + """Extracts the family and model based on vendorId + + :rtype: (ComputedFamily, ComputedModel) + """ + cpuid_res = do_cpuid(1) + if is_intel_proc(): + format = X86IntelCpuidFamilly + elif is_amd_proc(): + format = X86AmdCpuidFamilly + else: + raise NotImplementedError("Cannot get familly information of proc <{0}>".format(get_vendor_id())) + infos = format.from_buffer_copy(struct.pack("<I", cpuid_res.EAX)) + if infos.FamilyID == 0x6 or infos.FamilyID == 0x0F: + ComputedModel = infos.ModelID + (infos.ExtendedModel << 4) + else: + ComputedModel = infos.ModelID + if infos.FamilyID == 0x0F: + ComputedFamily = infos.FamilyID + infos.ExtendedFamily + else: + ComputedFamily = infos.FamilyID + return ComputedFamily, ComputedModel
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/native_exec/native_function.html b/docs/build/html/_modules/windows/native_exec/native_function.html new file mode 100644 index 0000000..57b3f95 --- /dev/null +++ b/docs/build/html/_modules/windows/native_exec/native_function.html @@ -0,0 +1,205 @@ + + + + + + + + windows.native_exec.native_function — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.native_exec.native_function

+import ctypes
+import mmap
+import platform
+import sys
+
+import windows
+import windows.winproxy
+
+from . import simple_x86 as x86
+from . import simple_x64 as x64
+
+
+class PyObj(ctypes.Structure):
+    _fields_ = [("ob_refcnt", ctypes.c_size_t),
+                ("ob_type", ctypes.c_void_p)]  # must be cast
+
+
+class PyMmap(PyObj):
+    _fields_ = [("ob_addr", ctypes.c_size_t), ("ob_size", ctypes.c_size_t)]
+
+
+# Specific mmap class for code injection
+class MyMap(mmap.mmap):
+    """ A mmap that is never unmapped and that contains the page address """
+    def __init__(self, *args, **kwarg):
+        # Get the page address by 'introspection' of the C struct
+        m = PyMmap.from_address(id(self))
+        self.addr = m.ob_addr
+        # Prevent garbage collection (so unmaping) of the page
+        m.ob_refcnt += 1
+
+    @classmethod
+    def get_map(cls, size):
+        """ Dispatch to the good mmap implem depending on the current system """
+        systems = {'windows': Win32MyMap,
+                   'linux': UnixMyMap}
+        x = platform.system().lower()
+        if x not in systems:
+            raise ValueError("Unknow system {0}".format(x))
+        return systems[x].get_map(size)
+
+
+class Win32MyMap(MyMap):
+    @classmethod
+    def get_map(cls, size):
+        addr = windows.winproxy.VirtualAlloc(0, size, 0x1000, 0x40)
+        new_map = (ctypes.c_char * size).from_address(addr)
+        new_map.addr = addr
+        if new_map.addr == 0:
+            raise ctypes.WinError()
+        return new_map
+
+
+class UnixMyMap(MyMap):
+    @classmethod
+    def get_map(cls, size):
+        prot = mmap.PROT_EXEC | mmap.PROT_WRITE | mmap.PROT_READ
+        return cls(-1, size, prot=prot)
+
+
+class CustomAllocator(object):
+    int_size = {'32bit': 4, '64bit': 8}
+
+    def __init__(self):
+        self.maps = []
+        self.get_new_page(0x1000)
+        self.names = []
+
+    @classmethod
+    def get_int_size(cls):
+        bits = platform.architecture()[0]
+        if bits not in cls.int_size:
+            raise ValueError("Unknow platform bits <{0}>".format(bits))
+        return cls.int_size[bits]
+
+    def get_new_page(self, size):
+        self.maps.append(MyMap.get_map(size))
+        self.cur_offset = 0
+        self.cur_page_size = size
+
+    def reserve_size(self, size):
+        if size + self.cur_offset > self.cur_page_size:
+            self.get_new_page((size + 0x1000) & ~0xfff)
+        addr = self.maps[-1].addr + self.cur_offset
+        self.cur_offset += size
+        return addr
+
+    def reserve_int(self, nb_int=1):
+        int_size = self.get_int_size()
+        return self.reserve_size(int_size * nb_int)
+
+    def write_code(self, code):
+        size = len(code)
+        if size + self.cur_offset > self.cur_page_size:
+            self.get_new_page((size + 0x1000) & ~0xfff)
+        self.maps[-1][self.cur_offset: self.cur_offset + size] = code
+        addr = self.maps[-1].addr + self.cur_offset
+        self.cur_offset += size
+        return addr
+
+allocator = CustomAllocator()
+
+
+
[docs]def create_function(code, types): + """Create a python function that call raw machine code + + :param str code: Raw machine code that will be called + :param list types: Return type and parameters type (see :mod:`ctypes`) + :return: the created function + :rtype: function + """ + func_type = ctypes.CFUNCTYPE(*types) + addr = allocator.write_code(code) + res = func_type(addr) + res.code_addr = addr + return res
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/native_exec/simple_x86.html b/docs/build/html/_modules/windows/native_exec/simple_x86.html new file mode 100644 index 0000000..bf69fab --- /dev/null +++ b/docs/build/html/_modules/windows/native_exec/simple_x86.html @@ -0,0 +1,1129 @@ + + + + + + + + windows.native_exec.simple_x86 — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.native_exec.simple_x86

+import collections
+import struct
+
+
+class BitArray(object):
+    def __init__(self, size, bits):
+        self.size = size
+        if len(bits) > size:
+            raise ValueError("size > len(bits)")
+
+        bits_list = []
+        for bit in bits:
+            x = int(bit)
+            if x not in [0, 1]:
+                raise ValueError("Not expected bits value {0}".format(x))
+            bits_list.append(x)
+
+        self.array = bits_list
+        if size > len(self.array):
+            self.array = ([0] * (size - len(self.array))) + self.array
+
+    def dump(self):
+        res = []
+        for i in range(self.size // 8):
+            c = 0
+            for x in (self.array[i * 8: (i + 1) * 8]):
+                c = (c << 1) + x
+            res.append(c)
+        return bytearray((res))
+
+    def __getitem__(self, slice):
+        return self.array[slice]
+
+    def __setitem__(self, slice, value):
+        self.array[slice] = value
+        return True
+
+    def __repr__(self):
+        return repr(self.array)
+
+    def __add__(self, other):
+        if not isinstance(other, BitArray):
+            return NotImplemented
+        return BitArray(self.size + other.size, self.array + other.array)
+
+    def to_int(self):
+        return int("".join([str(i) for i in self.array]), 2)
+
+    @classmethod
+    def from_string(cls, str_base):
+        l = []
+        for c in bytearray(reversed(str_base)):
+            for i in range(8):
+                l.append(c & 1)
+                c = c >> 1
+        return cls(len(str_base) * 8, list(reversed(l)))
+
+    @classmethod
+    def from_int(cls, size, x):
+        if x < 0:
+            x = x & ((2 ** size) - 1)
+        return cls(size, bin(x)[2:])
+
+
+# Prefix
+class Prefix(object):
+    PREFIX_VALUE = None
+
+    def __init__(self, next=None):
+        self.next = next
+
+    def __add__(self, other):
+        return type(self)(other)
+
+    def get_code(self):
+        return chr(self.PREFIX_VALUE) + self.next.get_code()
+
+
+def create_prefix(name, value):
+    prefix_type = type(name + "Type", (Prefix,), {'PREFIX_VALUE': value})
+    return prefix_type()
+
+LockPrefix = create_prefix('LockPrefix', 0xf0)
+Repne = create_prefix('Repne', 0xf2)
+Rep = create_prefix('Rep', 0xf3)
+SSPrefix = create_prefix('SSPrefix', 0x36)
+CSPrefix = create_prefix('CSPrefix', 0x2e)
+DSPrefix = create_prefix('DSPrefix', 0x3e)
+ESPrefix = create_prefix('ESPrefix', 0x26)
+FSPrefix = create_prefix('FSPrefix', 0x64)
+GSPrefix = create_prefix('GSPrefix', 0x65)
+OperandSizeOverride = create_prefix('OperandSizeOverride', 0x66)
+AddressSizeOverride = create_prefix('AddressSizeOverride', 0x67)
+
+# Main informations about X86
+mem_access = collections.namedtuple('mem_access', ['base', 'index', 'scale', 'disp', 'prefix'])
+x86_regs = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
+x86_16bits_regs = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI']
+
+x86_segment_selectors = {'CS': CSPrefix, 'DS': DSPrefix, 'ES': ESPrefix, 'SS': SSPrefix,
+                         'FS': FSPrefix, 'GS': GSPrefix}
+
+
+class X86(object):
+    @staticmethod
+    def is_reg(name):
+        try:
+            return name.upper() in x86_regs + x86_16bits_regs
+        except AttributeError:  # Not a string
+            return False
+
+    @staticmethod
+    def reg_size(name):
+        if name.upper() in x86_regs:
+            return 32
+        elif name.upper() in x86_16bits_regs:
+            return 16
+        else:
+            raise ValueError("Unknow register <{0}>".format(name))
+
+    @staticmethod
+    def is_mem_acces(data):
+        return isinstance(data, mem_access)
+
+    @staticmethod
+    def mem_access_has_only(mem_access, names):
+        if not X86.is_mem_acces(mem_access):
+            raise ValueError("mem_access_has_only")
+        for f in mem_access._fields:
+            v = getattr(mem_access, f)
+            if v and f != 'prefix' and f not in names:
+                return False
+            if v is None and f in names:
+                return False
+        return True
+
+
+
[docs]def create_displacement(base=None, index=None, scale=None, disp=0, prefix=None): + """Creates a X86 memory access description""" + if index is not None and scale is None: + scale = 1 + if scale and index is None: + raise ValueError("Cannot create displacement with scale and no index") + if scale and index.upper() == "ESP": + raise ValueError("Cannot create displacement with index == ESP") + return mem_access(base, index, scale, disp, prefix)
+ + +
[docs]def deref(disp): + """Create a memory access for an immediate value ``Ex: [0x42424242]``""" + return create_displacement(disp=disp)
+ + +
[docs]def mem(data): + """Parse a memory access string of format ``[EXPR]`` or ``seg:[EXPR]`` + + ``EXPR`` may describe: ``BASE | INDEX * SCALE | DISPLACEMENT`` or any combinaison (in this order) + """ + if not isinstance(data, str): + raise TypeError("mem need a string to parse") + data = data.strip() + prefix = None + if not (data.startswith("[") and data.endswith("]")): + if data[2] != ":": + raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]") + prefix_name = data[:2].upper() + if prefix_name not in x86_segment_selectors: + raise ValueError("Unknow segment selector {0}".format(prefix_name)) + prefix = prefix_name + data = data[3:] + if not (data.startswith("[") and data.endswith("]")): + raise ValueError("mem acces expect <[EXPR]> or <seg:[EXPR]") + # A l'arrache.. j'aime pas le parsing de trucs + data = data[1:-1] + items = data.split("+") + parsed_items = {'prefix': prefix} + for item in items: + item = item.strip() + # Index * scale + if "*" in item: + if 'index' in parsed_items: + raise ValueError("Multiple index / index*scale in mem expression <{0}>".format(data)) + sub_items = item.split("*") + if len(sub_items) != 2: + raise ValueError("Invalid item <{0}> in mem access".format(item)) + index, scale = sub_items + index, scale = index.strip(), scale.strip() + if not X86.is_reg(index): + raise ValueError("Invalid index <{0}> in mem access".format(index)) + if X86.reg_size(index) == 16: + raise NotImplementedError("16bits modrm") + try: + scale = int(scale, 0) + except ValueError: + raise ValueError("Invalid scale <{0}> in mem access".format(scale)) + parsed_items['scale'] = scale + parsed_items['index'] = index + else: + # displacement / base / index alone + if X86.is_reg(item): + if X86.reg_size(item) == 16: + raise NotImplementedError("16bits modrm") + if 'base' not in parsed_items: + parsed_items['base'] = item + continue + # Already have base + index -> cannot avec another register in expression + if 'index' in parsed_items: + raise ValueError("Multiple index / index*scale in mem expression <{0}>".format(data)) + parsed_items['index'] = item + continue + try: + disp = int(item, 0) + except ValueError: + raise ValueError("Invalid base/index or displacement <{0}> in mem access".format(item)) + if 'disp' in parsed_items: + raise ValueError("Multiple displacement in mem expression <{0}>".format(data)) + parsed_items['disp'] = disp + return create_displacement(**parsed_items)
+ + +# Helper to get the BitArray associated to a register +class X86RegisterSelector(object): + size = 3 # bits + reg_opcode = {v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_regs)} + reg_opcode.update({v: BitArray.from_int(size=3, x=i) for i, v in enumerate(x86_16bits_regs)}) + + def accept_arg(self, args, instr_state): + x = args[0] + try: + return (1, self.reg_opcode[x.upper()]) + except (KeyError, AttributeError): + return (None, None) + + @classmethod + def get_reg_bits(cls, name): + return cls.reg_opcode[name.upper()] + + +# Instruction Parameters +class FixedRegister(object): + def __init__(self, register): + self.reg = register.upper() + + def accept_arg(self, args, instr_state): + x = args[0] + if isinstance(x, str) and x.upper() == self.reg: + return (1, BitArray(0, [])) + return None, None + +RegisterEax = lambda: FixedRegister('EAX') + + +class RawBits(BitArray): + def accept_arg(self, args, instr_state): + return (0, self) + + +# Immediat value logic +# All 8/16 bits stuff are sign extended +class ImmediatOverflow(ValueError): + pass + + +def accept_as_8immediat(x): + try: + return struct.pack("<b", x) + except struct.error: + raise ImmediatOverflow("8bits signed Immediat overflow") + + +def accept_as_16immediat(x): + try: + return struct.pack("<h", x) + except struct.error: + raise ImmediatOverflow("16bits signed Immediat overflow") + + +def accept_as_unsigned_16immediat(x): + try: + return struct.pack("<H", x) + except struct.error: + raise ImmediatOverflow("16bits unsigned Immediat overflow") + +def accept_as_32immediat(x): + try: + return struct.pack("<i", x) + except struct.error: + pass + try: + return struct.pack("<I", x) + except struct.error: + raise ImmediatOverflow("32bits signed Immediat overflow") + + +class Imm8(object): + def accept_arg(self, args, instr_state): + try: + x = int(args[0]) + except (ValueError, TypeError): + return (None, None) + try: + imm8 = accept_as_8immediat(x) + except ImmediatOverflow: + return None, None + return (1, BitArray.from_string(imm8)) + + +class Imm16(object): + def accept_arg(self, args, instr_state): + try: + x = int(args[0]) + except (ValueError, TypeError): + return (None, None) + try: + imm16 = accept_as_16immediat(x) + except ImmediatOverflow: + return None, None + return (1, BitArray.from_string(imm16)) + +class UImm16(object): + def accept_arg(self, args, instr_state): + try: + x = int(args[0]) + except (ValueError, TypeError): + return (None, None) + try: + imm16 = accept_as_unsigned_16immediat(x) + except ImmediatOverflow: + return None, None + return (1, BitArray.from_string(imm16)) + + +class Imm32(object): + def accept_arg(self, args, instr_state): + try: + x = int(args[0]) + except (ValueError, TypeError): + return (None, None) + try: + imm32 = accept_as_32immediat(x) + except ImmediatOverflow: + return None, None + return (1, BitArray.from_string(imm32)) + +class SegmentSelectorAbsoluteAddr(object): + def accept_arg(self, args, instr_state): + sizess, datass = UImm16().accept_arg(args, instr_state) + if sizess is None: + return None, None + sizeabs, dataabs = Imm32().accept_arg(args[1:], instr_state) + if sizeabs is None: + return None, None + return (sizess + sizeabs, dataabs + datass) + + +class ModRM(object): + def __init__(self, sub_modrm, accept_reverse=True, has_direction_bit=True): + self.accept_reverse = accept_reverse + self.has_direction_bit = has_direction_bit + self.sub = sub_modrm + + def accept_arg(self, args, instr_state): + if len(args) < 2: + raise ValueError("Missing arg for modrm") + arg1 = args[0] + arg2 = args[1] + for sub in self.sub: + # Problem in reverse sens -> need to fix it + if sub.match(arg1, arg2): + d = sub(arg1, arg2, 0, instr_state) + if self.has_direction_bit: + instr_state.previous[0][-2] = d.direction + return (2, d.mod + d.reg + d.rm + d.after) + elif self.accept_reverse and sub.match(arg2, arg1): + d = sub(arg2, arg1, 1, instr_state) + if self.has_direction_bit: + instr_state.previous[0][-2] = d.direction + return (2, d.mod + d.reg + d.rm + d.after) + return (None, None) + + +class ModRM_REG__REG(object): + + @classmethod + def match(cls, arg1, arg2): + return X86.is_reg(arg1) and X86.is_reg(arg2) + + def __init__(self, arg1, arg2, reversed, instr_state): + self.mod = BitArray(2, "11") + if X86.reg_size(arg1) != X86.reg_size(arg2): + raise ValueError("Register size mitmatch between {0} and {1}".format(arg1, arg2)) + if X86.reg_size(arg1) == 16: + instr_state.prefixes.append(OperandSizeOverride) + self.reg = X86RegisterSelector.get_reg_bits(arg2) + self.rm = X86RegisterSelector.get_reg_bits(arg1) + self.after = BitArray(0, "") + self.direction = 0 + + +class ModRM_REG__MEM(object): + + @classmethod + def match(cls, arg1, arg2): + return X86.is_reg(arg1) and X86.is_mem_acces(arg2) + + def setup_reg_as_register(self, regname, instr_state): + self.reg = X86RegisterSelector.get_reg_bits(regname) + if X86.reg_size(regname) == 16: + instr_state.prefixes.append(OperandSizeOverride) + + def __init__(self, arg1, arg2, reversed, instr_state): + # ARG1 : REG + # ARG2 : prefix:[MEM] + # Handle prefix: + if arg2.prefix is not None: + instr_state.prefixes.append(x86_segment_selectors[arg2.prefix]) + if X86.mem_access_has_only(arg2, ["disp"]): + self.mod = BitArray(2, "00") + self.setup_reg_as_register(arg1, instr_state) + self.rm = BitArray(3, "101") + try: + self.after = BitArray.from_string(accept_as_32immediat(arg2.disp)) + except ImmediatOverflow: + raise ImmediatOverflow("Interger32 overflow for displacement {0}".format(hex(arg2.disp))) + self.direction = not reversed + return + # Those registers cannot be addressed without SIB + # No index -> no scale -> no SIB + FIRE_UP_SIB = (arg2.base and arg2.base.upper() in ["ESP", "EBP"]) or arg2.index + if not FIRE_UP_SIB: + self.setup_reg_as_register(arg1, instr_state) + self.rm = X86RegisterSelector.get_reg_bits(arg2.base) + self.compute_displacement(arg2.disp) + self.direction = not reversed + return + # FIRE UP THE SIB + # Handle no base and base == EBP special case + if not arg2.base: + force_displacement = 4 + elif arg2.base.upper() == "EBP": + force_displacement = 1 + else: + force_displacement = 0 + + self.setup_reg_as_register(arg1, instr_state) + self.rm = BitArray(3, "100") + self.compute_displacement(arg2.disp, force_displacement) + self.after = self.compute_sib(arg2) + self.after + if not arg2.base: + self.mod = BitArray(2, "00") + self.direction = not reversed + + def compute_displacement(self, displacement, force_displacement=0): + if not displacement and not force_displacement: + self.mod = BitArray(2, "00") + self.after = BitArray(0, "") + return + # Pack in a byte + try: + v = accept_as_8immediat(displacement) + except ImmediatOverflow: + v = None + if v is not None and force_displacement <= 1: + self.mod = BitArray(2, "01") + self.after = BitArray.from_string(v) + return + # Pack in a dword + try: + v = accept_as_32immediat(displacement) + except ImmediatOverflow: + v = None + if v is not None and force_displacement <= 4: + self.mod = BitArray(2, "10") + self.after = BitArray.from_string(v) + return + raise ValueError("Displacement {0} is too big".format(hex(displacement))) + + def compute_sib(self, mem_access): + scale = {1: 0, 2: 1, 4: 2, 8: 3} + if mem_access.index is None: + return BitArray(2, "00") + BitArray(3, "100") + X86RegisterSelector.get_reg_bits(mem_access.base) + if mem_access.scale not in scale: + raise ValueError("Invalid scale for mem access <{0}>".format(mem_access.scale)) + if mem_access.base is None: + return BitArray.from_int(2, scale[mem_access.scale]) + X86RegisterSelector.get_reg_bits(mem_access.index) + BitArray(3, "101") + return BitArray.from_int(2, scale[mem_access.scale]) + X86RegisterSelector.get_reg_bits(mem_access.index) + X86RegisterSelector.get_reg_bits(mem_access.base) + + +class Slash(object): + "No idea for the name: represent the modRM for single args + encoding in reg (/7 in cmp in man intel)" + + def __init__(self, reg_num): + "reg = 7 for /7" + self.reg = x86_regs[reg_num] + + def accept_arg(self, args, instr_state): + if len(args) < 1: + raise ValueError("Missing arg for Slash") + # Reuse all the MODRm logique with the reg as our self.reg + # The sens of param is strange I need to fix the `reversed` logique + arg_consum, value = ModRM([ModRM_REG__REG, ModRM_REG__MEM], has_direction_bit=False).accept_arg(args[:1] + [self.reg] + args[1:], instr_state) + if value is None: + return arg_consum, value + return arg_consum - 1, value + +class ControlRegisterModRM(object): + def __init__(self, writecr = False): + self.writecr = writecr + + def accept_arg(self, args, instr_state): + writecr = self.writecr + if len(args) < 2: + return None, None + reg = args[writecr] + cr = args[not writecr] + if not isinstance(cr, str): + return None, None + if not cr.lower().startswith("cr"): + return None, None + try: + cr_number = int(cr[2:], 10) + except ValueError as e: + raise ValueError("Invalid ControlRegister {0}".format(cr)) + if cr_number > 7: + raise ValueError("Invalid ControlRegister {0}".format(cr)) + + modrm_params = [reg, x86_regs[cr_number]] + args[2:] + return ModRM([ModRM_REG__REG], has_direction_bit=False).accept_arg(modrm_params, instr_state) + + +instr_state = collections.namedtuple('instr_state', ['previous', 'prefixes']) + +class Instruction(object): + """Base class of instructions, use `encoding` to find a valid way to assemble the instruction""" + encoding = [] + + def __init__(self, *initial_args): + for type_encoding in self.encoding: + args = list(initial_args) + prefix = [] + res = [] + for element in type_encoding: + arg_consum, value = element.accept_arg(args, instr_state(res, prefix)) + if arg_consum is None: + break + res.append(value) + del args[:arg_consum] + else: # if no break + if args: # if still args: fail + continue + self.value = sum(res, BitArray(0, "")) + self.prefix = prefix + return + raise ValueError("Cannot encode <{0} {1}>:(".format(type(self).__name__, initial_args)) + + def get_code(self): + prefix_opcode = b"".join(chr(p.PREFIX_VALUE) for p in self.prefix) + return prefix_opcode + bytes(self.value.dump()) + + #def __add__(self, other): + # res = MultipleInstr() + # res += self + # res += other + # return res + + def __mul__(self, value): + if not isinstance(value, (int, long)): + return NotImplemented + res = MultipleInstr() + for i in range(value): + res += self + return res + + +# Jump helpers +class DelayedJump(object): + """A jump to a label :NAME""" + + def __init__(self, type, label): + self.type = type + self.label = label + + +class JmpType(Instruction): + """Dispatcher between a real jump or DelayedJump if parameters is a label""" + + def __new__(cls, *initial_args): + if len(initial_args) == 1: + arg = initial_args[0] + if isinstance(arg, str) and arg[0] == ":": + return DelayedJump(cls, arg) + return super(JmpType, cls).__new__(cls, *initial_args) + + +class JmpImm(object): + """Immediat parameters for Jump instruction + Sub a specified size from the size to jump to `emulate` a jump from the begin address of the instruction""" + accept_as_Ximmediat = None + + def __init__(self, sub): + self.sub = sub + + def accept_arg(self, args, instr_state): + try: + jump_size = int(args[0]) + except (ValueError, TypeError): + return (None, None) + jump_size -= self.sub + try: + jmp_imm = self.accept_as_Ximmediat(jump_size) + except ImmediatOverflow: + return (None, None) + return (1, BitArray.from_string(jmp_imm)) + + +class JmpImm8(JmpImm): + accept_as_Ximmediat = staticmethod(accept_as_8immediat) + + +class JmpImm32(JmpImm): + accept_as_Ximmediat = staticmethod(accept_as_32immediat) + + +# Instructions + +class Call(JmpType): + encoding = [(RawBits.from_int(8, 0xe8), JmpImm32(5)), + (RawBits.from_int(8, 0xff), Slash(2)), + (RawBits.from_int(8, 0x9a), SegmentSelectorAbsoluteAddr())] + +class Jmp(JmpType): + encoding = [(RawBits.from_int(8, 0xeb), JmpImm8(2)), + (RawBits.from_int(8, 0xe9), JmpImm32(5)), + (RawBits.from_int(8, 0xea), SegmentSelectorAbsoluteAddr())] + + +class Jz(JmpType): + encoding = [(RawBits.from_int(8, 0x74), JmpImm8(2)), + (RawBits.from_int(16, 0x0f84), JmpImm32(6))] + + +class Jnz(JmpType): + encoding = [(RawBits.from_int(8, 0x75), JmpImm8(2)), + (RawBits.from_int(16, 0x0f85), JmpImm32(6))] + + +class Jbe(JmpType): + encoding = [(RawBits.from_int(8, 0x76), JmpImm8(2)), + (RawBits.from_int(16, 0x0f86), JmpImm32(6))] + + +class Jnb(JmpType): + encoding = [(RawBits.from_int(8, 0x73), JmpImm8(2)), + (RawBits.from_int(16, 0x0f83), JmpImm32(6))] + + +class Push(Instruction): + encoding = [(RawBits.from_int(5, 0x50 >> 3), X86RegisterSelector()), + (RawBits.from_int(8, 0x68), Imm32()), + (RawBits.from_int(8, 0xff), Slash(6))] + + +class Pop(Instruction): + encoding = [(RawBits.from_int(5, 0x58 >> 3), X86RegisterSelector())] + + +class Dec(Instruction): + encoding = [(RawBits.from_int(5, 0x48 >> 3), X86RegisterSelector())] + + +class Inc(Instruction): + encoding = [(RawBits.from_int(5, 0x40 >> 3), X86RegisterSelector()), + (RawBits.from_int(8, 0xff), Slash(0))] + + +class Add(Instruction): + encoding = [(RawBits.from_int(8, 0x05), RegisterEax(), Imm32()), + (RawBits.from_int(8, 0x81), Slash(0), Imm32()), + (RawBits.from_int(8, 0x01), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + +class And(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(8, 0x25), RegisterEax(), Imm32()), + (RawBits.from_int(8, 0x81), Slash(4), Imm32()), + (RawBits.from_int(8, 0x21), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + + +class Or(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(8, 0x0d), RegisterEax(), Imm32()), + (RawBits.from_int(8, 0x81), Slash(1), Imm32()), + (RawBits.from_int(8, 0x09), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + + +class Sub(Instruction): + encoding = [(RawBits.from_int(8, 0x2D), RegisterEax(), Imm32()), + (RawBits.from_int(8, 0x81), Slash(5), Imm32()), + (RawBits.from_int(8, 0x29), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + + +class Mov(Instruction): + encoding = [(RawBits.from_int(8, 0x89), ModRM([ModRM_REG__REG, ModRM_REG__MEM])), + (RawBits.from_int(8, 0xc7), Slash(0), Imm32()), + (RawBits.from_int(5, 0xb8 >> 3), X86RegisterSelector(), Imm32()), + (RawBits.from_int(16, 0x0f20), ControlRegisterModRM(writecr=False)), + (RawBits.from_int(16, 0x0f22), ControlRegisterModRM(writecr=True))] + + +class Movsb(Instruction): + encoding = [(RawBits.from_int(8, 0xa4),)] + + +class Movsd(Instruction): + encoding = [(RawBits.from_int(8, 0xa5),)] + + +class Lea(Instruction): + encoding = [(RawBits.from_int(8, 0x8d), ModRM([ModRM_REG__MEM], accept_reverse=False, has_direction_bit=False))] + + +class Cmp(Instruction): + encoding = [(RawBits.from_int(8, 0x3d), RegisterEax(), Imm32()), + (RawBits.from_int(8, 0x81), Slash(7), Imm32()), + (RawBits.from_int(8, 0x3b), ModRM([ModRM_REG__REG, ModRM_REG__MEM]))] + + +class Test(Instruction): + encoding = [(RawBits.from_int(8, 0xf7), Slash(7), Imm32()), + (RawBits.from_int(8, 0x85), ModRM([ModRM_REG__REG, ModRM_REG__MEM], has_direction_bit=False))] + + +class Out(Instruction): + encoding = [(RawBits.from_int(8, 0xee), FixedRegister('DX'), FixedRegister('AL')), + (RawBits.from_int(16, 0x66ef), FixedRegister('DX'), FixedRegister('AX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(8, 0xef), FixedRegister('DX'), FixedRegister('EAX'))] + + +class In(Instruction): + encoding = [(RawBits.from_int(8, 0xec), FixedRegister('AL'), FixedRegister('DX')), + (RawBits.from_int(16, 0x66ed), FixedRegister('AX'), FixedRegister('DX')), # Fuck-it hardcoded prefix for now + (RawBits.from_int(8, 0xed), FixedRegister('EAX'), FixedRegister('DX'))] + + +class Xor(Instruction): + encoding = [(RawBits.from_int(8, 0x31), ModRM([ModRM_REG__REG]))] + + +class Xchg(Instruction): + encoding = [(RawBits.from_int(5, 0x90 >> 3), RegisterEax(), X86RegisterSelector()), (RawBits.from_int(5, 0x90 >> 3), X86RegisterSelector(), RegisterEax())] + + +class Rol(Instruction): + encoding = [(RawBits.from_int(8, 0xC1), Slash(0), Imm8())] + +class Ror(Instruction): + encoding = [(RawBits.from_int(8, 0xC1), Slash(1), Imm8())] + +class Shr(Instruction): + encoding = [(RawBits.from_int(8, 0xC1), Slash(5), Imm8())] + +class Shl(Instruction): + encoding = [(RawBits.from_int(8, 0xC1), Slash(4), Imm8())] + +class Cpuid(Instruction): + encoding = [(RawBits.from_int(16, 0x0fa2),)] + + +class Ret(Instruction): + encoding = [(RawBits.from_int(8, 0xc3),)] + + +class ScasB(Instruction): + encoding = [(RawBits.from_int(8, 0xAE),)] + +class ScasW(Instruction): + encoding = [(RawBits.from_int(16, 0x66AF),)] + +class ScasD(Instruction): + encoding = [(RawBits.from_int(8, 0xAF),)] + + +class CmpsB(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(8, 0xa6),)] + + +class CmpsW(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(16, 0x66A7),)] + + +class CmpsD(Instruction): + default_32_bits = True + encoding = [(RawBits.from_int(8, 0xa7),)] + + +class Nop(Instruction): + encoding = [(RawBits.from_int(8, 0x90),)] + +class Not(Instruction): + encoding = [(RawBits.from_int(8, 0xF7), Slash(2))] + +class Retf(Instruction): + encoding = [(RawBits.from_int(8, 0xcb),)] + + +class Int3(Instruction): + encoding = [(RawBits.from_int(8, 0xcc),)] + + +class _NopArtifact(Nop): + """Special NOP used in shellcode reduction""" + pass + + +class Label(object): + + def __init__(self, name): + self.name = name + + +def JmpAt(addr): + code = MultipleInstr() + code += Push(addr) + code += Ret() + return code + + +class MultipleInstr(object): + JUMP_SIZE = 6 + + def __init__(self, init_instrs=()): + self.instrs = {} + self.labels = {} + self.expected_labels = {} + # List of all labeled jump already resolved + # Will be used for 'relocation' + self.computed_jump = [] + self.size = 0 + for i in init_instrs: + self += i + + def get_code(self): + if self.expected_labels: + raise ValueError("Unresolved labels: {0}".format(self.expected_labels.keys())) + return b"".join([x[1].get_code() for x in sorted(self.instrs.items())]) + + def add_instruction(self, instruction): + if isinstance(instruction, Label): + return self.add_label(instruction) + # Change DelayedJump to LabeledJump ? + if isinstance(instruction, DelayedJump): + return self.add_delayed_jump(instruction) + if isinstance(instruction, (Instruction, Prefix)): + self.instrs[self.size] = instruction + self.size += len(instruction.get_code()) + return + raise ValueError("Don't know what to do with {0} of type {1}".format(instruction, type(instruction))) + + def add_label(self, label): + if label.name not in self.expected_labels: + # Label that have no jump before definition + # Just registed the address of the label + self.labels[label.name] = self.size + return + # Label with jmp before definition + # Lot of stuff todo: + # Find all delayed jump that refer to this jump + # Replace them with real jump + # If size of jump < JUMP_SIZE: relocate everything we can + # Update expected_labels + for jump_to_label in self.expected_labels[label.name]: + if jump_to_label.offset in self.instrs: + raise ValueError("WTF REPLACE EXISTING INSTR...") + distance = self.size - jump_to_label.offset + real_jump = jump_to_label.type(distance) + self.instrs[jump_to_label.offset] = real_jump + self.computed_jump.append((jump_to_label.offset, self.size)) + for i in range(self.JUMP_SIZE - len(real_jump.get_code())): + self.instrs[jump_to_label.offset + len(real_jump.get_code()) + i] = _NopArtifact() + del self.expected_labels[label.name] + self.labels[label.name] = self.size + if not self.expected_labels: + # No more un-resolved label (for now): time to reduce the shellcode + self._reduce_shellcode() + + def add_delayed_jump(self, jump): + dst = jump.label + if dst in self.labels: + # Jump to already defined labels + # Nothing fancy: get offset of label and jump to it ! + distance = self.size - self.labels[dst] + jump_instruction = jump.type(-distance) + self.computed_jump.append((self.size, self.labels[dst])) + return self.add_instruction(jump_instruction) + # Jump to undefined label + # Add label to expected ones + # Add jump info -> offset of jump | type + # Reserve space for call ! + jump.offset = self.size + self.expected_labels.setdefault(dst, []).append(jump) + self.size += self.JUMP_SIZE + return + + def _reduce_shellcode(self): + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] + while to_remove: + self._remove_nop_artifact(to_remove[0]) + # _remove_nop_artifact will change the offsets of the nop + # Need to refresh these offset + to_remove = [offset for offset, instr in self.instrs.items() if type(instr) == _NopArtifact] + + def _remove_nop_artifact(self, offset): + # Remove a NOP from the shellcode + for src, dst in self.computed_jump: + # Reduce size of Jump over the nop (both sens) + if src < offset < dst or dst < offset < src: + old_jmp = self.instrs[src] + old_jump_size = len(old_jmp.get_code()) + if src < offset < dst: + new_jmp = type(old_jmp)(dst - src - 1) + else: + new_jmp = type(old_jmp)(dst - src + 1) + new_jmp_size = len(new_jmp.get_code()) + if new_jmp_size > old_jump_size: + raise ValueError("Wtf jump of smaller size is bigger.. ABORT") + self.instrs[src] = new_jmp + # Add other _NopArtifact if jump instruction size is reduced + for i in range(old_jump_size - new_jmp_size): + self.instrs[src + new_jmp_size + i] = _NopArtifact() + + # dec offset of all Label after the NOP + for name, labeloffset in self.labels.items(): + if labeloffset > offset: + self.labels[name] = labeloffset - 1 + + # dec offset of all instr after the NOP + new_instr = {} + for instroffset, instr in self.instrs.items(): + if instroffset == offset: + continue + if instroffset > offset: + instroffset -= 1 + new_instr[instroffset] = instr + self.instrs = new_instr + # Update all computed jump + new_computed_jump = [] + for src, dst in self.computed_jump: + if src > offset: + src -= 1 + if dst > offset: + dst -= 1 + new_computed_jump.append((src, dst)) + self.computed_jump = new_computed_jump + # dec size of the shellcode + self.size -= 1 + + def merge_shellcode(self, other): + shared_labels = set(self.labels) & set(other.labels) + if shared_labels: + raise ValueError("Cannot merge shellcode: shared labels {0}".format(shared_labels)) + for offset, instr in sorted(other.instrs.items()): + for label_name in [name for name, label_offset in other.labels.items() if label_offset == offset]: + self.add_instruction(Label(label_name)) + self.add_instruction(instr) + + def __iadd__(self, other): + if isinstance(other, MultipleInstr): + self.merge_shellcode(other) + else: + self.add_instruction(other) + return self + + +def split_in_instruction(str): + for line in str.split("\n"): + if not line: + continue + for instr in line.split(";"): + if not instr: + continue + yield instr.strip() + +def assemble(str): + """Play test""" + shellcode = MultipleInstr() + for instr in split_in_instruction(str): + data = instr.split(" ", 1) + mnemo, args_raw = data[0], data[1:] + try: + instr_object = globals()[mnemo.capitalize()] + except: + raise ValueError("Unknow mnemonic <{0}>".format(mnemo)) + + args = [] + if args_raw: + for arg in args_raw[0].split(","): + arg = arg.strip() + if (arg[0] == "[" or arg[2:4] == ":[") and arg[-1] == "]": + arg = mem(arg) + else: + try: + arg = int(arg, 0) + except ValueError: + pass + args.append(arg) + shellcode += instr_object(*args) + return shellcode.get_code() + +# IDA : import windows.native_exec.simple_x86 as x86 +# IDA testing + +try: + import midap + import idc + in_IDA = True +except ImportError: + in_IDA = False + + +if in_IDA: + def test_code(): + s = MultipleInstr() + s += Mov("Eax", "ESI") + s += Inc("Ecx") + s += Dec("edi") + s += Ret() + return s + + def reset(): + idc.MakeUnknown(idc.MinEA(), 0x1000, 0) + for i in range(0x1000): + idc.PatchByte(idc.MinEA() + i, 0) + + s = test_code() + + def tst(): + reset() + midap.here(idc.MinEA()).write(s.get_code()) + idc.MakeFunction(idc.MinEA()) +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/pe_parse.html b/docs/build/html/_modules/windows/pe_parse.html new file mode 100644 index 0000000..da3f2e5 --- /dev/null +++ b/docs/build/html/_modules/windows/pe_parse.html @@ -0,0 +1,471 @@ + + + + + + + + windows.pe_parse — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.pe_parse

+import ctypes
+import windows
+import windows.hooks as hooks
+import windows.utils as utils
+
+from windows.generated_def.winstructs import *
+from windows.utils import transform_ctypes_fields
+import windows.remotectypes as rctypes
+
+# This must go to windefs
+IMAGE_DIRECTORY_ENTRY_EXPORT = 0
+IMAGE_DIRECTORY_ENTRY_IMPORT = 1
+
+IMAGE_ORDINAL_FLAG32 = 0x80000000
+IMAGE_ORDINAL_FLAG64 = 0x8000000000000000
+
+
+def get_structure_transformer_for_target(target, targetbitness=None):
+    current_bitness = windows.current_process.bitness
+    if target is None:
+        ctypes_structure_transformer = lambda x:x
+        create_structure_at = lambda structcls, addr: structcls.from_address(addr)
+        return ctypes_structure_transformer, create_structure_at
+
+    if targetbitness is None:
+        targetbitness = target.bitness
+
+    if targetbitness == 32 and current_bitness == 64:
+        ctypes_structure_transformer = rctypes.transform_type_to_remote32bits
+    elif targetbitness == 64 and current_bitness == 32:
+        ctypes_structure_transformer = rctypes.transform_type_to_remote64bits
+    elif targetbitness == current_bitness:
+        ctypes_structure_transformer = rctypes.transform_type_to_remote
+    else:
+        raise NotImplementedError("Parsing {0} PE from {1} Process".format(targetedbitness, proc_bitness))
+
+    def create_structure_at(structcls, addr):
+        return ctypes_structure_transformer(structcls)(addr, target)
+    return ctypes_structure_transformer, create_structure_at
+
+def get_pe_bitness(baseaddr, target):
+    # We can force bitness as the filed we access are bitness-independant
+    pe = GetPEFile(baseaddr, target, force_bitness=32)
+    machine = pe.get_NT_HEADER().FileHeader.Machine
+    if machine == 0x14c:
+        return 32
+    elif machine == 0x8664:
+        return 64
+    else:
+        raise ValueError("Unknow PE target machine <0x{0:x}>".format(machine))
+
+
+
[docs]def GetPEFile(baseaddr, target=None, force_bitness=None): + """Returns a :class:`PEFile` to explore a PE loaded at `baseaddr` in process `target`. + + :rtype: :class:`PEFile` + + .. note:: + + If target is ``None`` it refers to the curent process + """ + proc_bitness = windows.current_process.bitness + + if force_bitness is None: + targetedbitness = get_pe_bitness(baseaddr, target) + else: + targetedbitness = force_bitness + + transformers = get_structure_transformer_for_target(target, targetedbitness) + ctypes_structure_transformer, create_structure_at = transformers + + if targetedbitness == 32: + IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG32 + else: + IMAGE_ORDINAL_FLAG = IMAGE_ORDINAL_FLAG64 + + def get_string(addr): + if target is None: + return ctypes.c_char_p(addr).value + return target.read_string(addr) + + class RVA(DWORD): + @property + def addr(self): + return baseaddr + self.value + + def __repr__(self): + return "<DWORD {0} (RVA to '{1}')>".format(self.value, hex(self.addr)) + + class StringRVa(RVA): + if target is None: + @property + def str(self): + return get_string(self.addr).decode() + else: + @property + def str(self): + return get_string(self.addr).decode() + + def __repr__(self): + return "<DWORD {0} (String RVA to '{1}')>".format(self.value, self.str) + + def __int__(self): + return self.value + + class IMPORT_BY_NAME(ctypes.Structure): + _fields_ = [ + ("Hint", WORD), + ("Name", BYTE) + ] + + class THUNK_DATA(ctypes.Union): + _fields_ = [ + ("Ordinal", PVOID), + ("AddressOfData", PVOID) + ] + + class IATEntry(ctypes.Structure): + """Represent an entry in the IAT of a module + Can be used to get resolved value and setup hook + """ + _fields_ = [ + ("value", PVOID)] + + + + @classmethod + def create(cls, addr, ord, name): + self = create_structure_at(cls, addr) + self.addr = addr + self.ord = ord + self.name = name + self.hook = None + self.nonhookvalue = self.value + return self + + def __repr__(self): + return '<{0} "{1}" ordinal {2}>'.format(self.__class__.__name__, self.name, self.ord) + + def set_hook(self, callback, types=None): + """Setup a hook on the entry and return it. + You MUST keep a reference to the hook while the hook is enabled. + + :param callback: the hook + + .. note:: + + see :ref:`hook_protocol` + + :rtype: :class:`windows.hooks.IATHook` + + .. warning:: + + This works only for PEFile with the current process as target. + """ + if target is not None: + raise NotImplementedError("Setting hook in remote process (use python code injection)") + + hook = hooks.IATHook(self, callback, types) + self.hook = hook + hook.enable() + return hook + + def remove_hook(self): + """Remove the hook on the entry""" + if self.hook is None: + return False + self.hook.disable() + self.hook = None + return True + + class PEFile(object): + """Represent a PE loaded in a process (current or remote)""" + def __init__(self): + self.baseaddr = baseaddr + self.bitness = targetedbitness + + def get_DOS_HEADER(self): + return create_structure_at(IMAGE_DOS_HEADER, baseaddr) + + def get_NT_HEADER(self): + return self.get_DOS_HEADER().get_NT_HEADER() + + def get_OptionalHeader(self): + return self.get_NT_HEADER().OptionalHeader + + def get_DataDirectory(self): + # This won't work if we load a PE32 in a 64bit process + # PE32 .NET... + #return self.get_OptionalHeader().DataDirectory + DataDirectory_type = IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES + SizeOfOptionalHeader = self.get_NT_HEADER().FileHeader.SizeOfOptionalHeader + if target is None: + opt_header_addr = ctypes.addressof(self.get_NT_HEADER().OptionalHeader) + else: + opt_header_addr = self.get_NT_HEADER().OptionalHeader._base_addr + DataDirectory_addr = opt_header_addr + SizeOfOptionalHeader - ctypes.sizeof(DataDirectory_type) + return create_structure_at(DataDirectory_type, DataDirectory_addr) + + + def get_IMPORT_DESCRIPTORS(self): + import_datadir = self.get_DataDirectory()[IMAGE_DIRECTORY_ENTRY_IMPORT] + if import_datadir.VirtualAddress == 0: + return [] + import_descriptor_addr = RVA(import_datadir.VirtualAddress).addr + current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr) + res = [] + while current_import_descriptor.FirstThunk: + res.append(current_import_descriptor) + import_descriptor_addr += ctypes.sizeof(self.IMAGE_IMPORT_DESCRIPTOR) + current_import_descriptor = create_structure_at(self.IMAGE_IMPORT_DESCRIPTOR, import_descriptor_addr) + return res + + def get_EXPORT_DIRECTORY(self): + export_directory_rva = self.get_DataDirectory()[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + if export_directory_rva == 0: + return None + export_directory_addr = baseaddr + export_directory_rva + return create_structure_at(self._IMAGE_EXPORT_DIRECTORY, export_directory_addr) + + class PESection((IMAGE_SECTION_HEADER)): + if target is None: + @property + def name(self): + return get_string(ctypes.addressof(self.Name))[:8] + else: + @property + def name(self): + return get_string(self._base_addr)[:8] + + @property + def start(self): + return baseaddr + self.VirtualAddress + + @property + def size(self): + return self.VirtualSize + + def __repr__(self): + return "<PESection \"{0}\">".format(self.name) + + @utils.fixedpropety + def sections(self): + nt_header = self.get_NT_HEADER() + nb_section = nt_header.FileHeader.NumberOfSections + SizeOfOptionalHeader = self.get_NT_HEADER().FileHeader.SizeOfOptionalHeader + if target is None: + opt_header_addr = ctypes.addressof(self.get_NT_HEADER().OptionalHeader) + else: + opt_header_addr = self.get_NT_HEADER().OptionalHeader._base_addr + base_section = opt_header_addr + SizeOfOptionalHeader + sections_array = create_structure_at((self.PESection * nb_section), base_section) + return list(sections_array) + + @utils.fixedpropety + def exports(self): + """The exports of the PE in a dict. Keys are ordinal (:class:`int`) and name (:class:`str`). + The values are the addresses of the exports. + + :type: {(:class:`int` or :class:`str`) : :class:`int`}""" + res = {} + exp_dir = self.get_EXPORT_DIRECTORY() + if exp_dir is None: + return res + raw_exports = exp_dir.get_exports() + for id, rva_addr, rva_name in raw_exports: + res[id] = rva_addr.addr + if rva_name is not None: + res[rva_name.str] = rva_addr.addr + return res + + @utils.fixedpropety + def export_name(self): + """The Name attribute of the ``EXPORT_DIRECTORY``""" + try: + return self.get_EXPORT_DIRECTORY().Name.str + except AttributeError: + return None + + # TODO: get imports by parsing other modules exports if no INT + @utils.fixedpropety + def imports(self): + """The imports of the PE in a dict. + Keys are the names of DLL to import from and values are :class:`list` + of :class:`IATEntry` + + :type: {:class:`str` : [:class:`IATEntry`]}""" + res = {} + for import_descriptor in self.get_IMPORT_DESCRIPTORS(): + INT = import_descriptor.get_INT() + IAT = import_descriptor.get_IAT() + if INT is not None: + for iat_entry, (ord, name) in zip(IAT, INT): + # str(name.decode()) -> python2 and python3 compatible for str result + iat_entry.ord = ord + iat_entry.name = str(name.decode()) if name else "" + res.setdefault(import_descriptor.Name.str.lower(), []).extend(IAT) + return res + + # Will be usable as `self.IMAGE_IMPORT_DESCRIPTOR` + class IMAGE_IMPORT_DESCRIPTOR(ctypes.Structure): + _fields_ = transform_ctypes_fields(IMAGE_IMPORT_DESCRIPTOR, {"Name": StringRVa, "OriginalFirstThunk": RVA, "FirstThunk": RVA}) + + def get_INT(self): + if not self.OriginalFirstThunk.value: + return None + int_addr = self.OriginalFirstThunk.addr + int_entry = create_structure_at(THUNK_DATA, int_addr) + res = [] + while int_entry.Ordinal: + if int_entry.Ordinal & IMAGE_ORDINAL_FLAG: + res += [(int_entry.Ordinal & 0x7fffffff, None)] + else: + import_by_name = create_structure_at(IMPORT_BY_NAME, baseaddr + int_entry.AddressOfData) + name_address = baseaddr + int_entry.AddressOfData + type(import_by_name).Name.offset + if target is None: + name = get_string(name_address) + else: + name = get_string(name_address).decode() + res.append((import_by_name.Hint, name)) + int_addr += ctypes.sizeof(type(int_entry)) + int_entry = create_structure_at(THUNK_DATA, int_addr) + return res + + def get_IAT(self): + iat_addr = self.FirstThunk.addr + iat_entry = create_structure_at(THUNK_DATA, iat_addr) + res = [] + while iat_entry.Ordinal: + res.append(IATEntry.create(iat_addr, -1, "??")) + iat_addr += ctypes.sizeof(type(iat_entry)) + iat_entry = create_structure_at(THUNK_DATA, iat_addr) + return res + + # Will be usable as `self._IMAGE_EXPORT_DIRECTORY` + class _IMAGE_EXPORT_DIRECTORY(ctypes.Structure): + _fields_ = transform_ctypes_fields(IMAGE_EXPORT_DIRECTORY, {"Name": StringRVa, "AddressOfFunctions": RVA, "AddressOfNames": RVA, "AddressOfNameOrdinals": RVA}) + + def get_exports(self): + NameOrdinals = create_structure_at((WORD * self.NumberOfNames), self.AddressOfNameOrdinals.addr) + NameOrdinals = list(NameOrdinals) + Functions = create_structure_at((RVA * self.NumberOfFunctions), self.AddressOfFunctions.addr) + Names = create_structure_at((StringRVa * self.NumberOfNames), self.AddressOfNames.addr) + res = [] + for nb, func in enumerate(Functions): + if nb in NameOrdinals: + name = Names[NameOrdinals.index(nb)] + else: + name = None + res.append((nb, func, name)) + return res + + current_pe = PEFile() + + class IMAGE_DOS_HEADER(ctypes.Structure): + _fields_ = [ + ("e_magic", CHAR * 2), + ("e_cblp", WORD), + ("e_cp", WORD), + ("e_crlc", WORD), + ("e_cparhdr", WORD), + ("e_minalloc", WORD), + ("e_maxalloc", WORD), + ("e_ss", WORD), + ("e_sp", WORD), + ("e_csum", WORD), + ("e_ip", WORD), + ("e_cs", WORD), + ("e_lfarlc", WORD), + ("e_ovno", WORD), + ("e_res", WORD * 4), + ("e_oemid", WORD), + ("e_oeminfo", WORD), + ("e_res2", WORD * 10), + ("e_lfanew", DWORD), + ] + + def get_NT_HEADER(self): + if targetedbitness == 32: + return create_structure_at(IMAGE_NT_HEADERS32, baseaddr + self.e_lfanew) + return create_structure_at(IMAGE_NT_HEADERS64, baseaddr + self.e_lfanew) + return current_pe
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/remotectypes.html b/docs/build/html/_modules/windows/remotectypes.html new file mode 100644 index 0000000..c5649eb --- /dev/null +++ b/docs/build/html/_modules/windows/remotectypes.html @@ -0,0 +1,554 @@ + + + + + + + + windows.remotectypes — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.remotectypes

+"""remote ctypes, a try to a ctypes wrapper that accept a target object for every ready operation
+Some code is copy-paste, might be userful to rewrite some part later"""
+
+import _ctypes
+import ctypes
+import ctypes.wintypes
+import itertools
+from _ctypes import _SimpleCData
+
+
+# ## Utils ### #
+def is_pointer(x):
+    return isinstance(x, _ctypes._Pointer)
+
+
+def is_pointer_type(x):
+    return issubclass(x, _ctypes._Pointer)
+
+
+def is_array(x):
+    return isinstance(x, _ctypes.Array)
+
+
+def is_array_type(x):
+    return issubclass(x, _ctypes.Array)
+
+
+def is_structure_type(x):
+    return issubclass(x, ctypes.Structure)
+
+
+def is_union_type(x):
+    return issubclass(x, ctypes.Union)
+
+# ### My types ### #
+
+# # 64bits pointer types # #
+
+# I know direct inheritance from _SimpleCData seems bad
+# But it seems to be the only way to have the normal
+# ctypes.Structure way of working (need to investigate)
+
+
+class c_void_p64(_SimpleCData):
+    _type_ = "Q"
+
+
+class c_char_p64(_SimpleCData):
+    _type_ = "Q"
+
+
+class c_wchar_p64(_SimpleCData):
+    _type_ = "Q"
+
+
+# # 32bits pointer types # #
+class c_void_p32(_SimpleCData):
+    _type_ = "I"
+
+
+class c_char_p32(_SimpleCData):
+    _type_ = "I"
+
+
+class c_wchar_p32(_SimpleCData):
+    _type_ = "I"
+
+
+# standard type translation
+# don't know how to handle size_t since it's non-distinguable from c_ulong
+# maybe force import before ctypes and modif stuff into ctypes ?
+
+
+# # Remote Value
+# Used by the RemoteStructure to access the target memory
+
+class RemoteValue(object):
+    @classmethod
+    def from_buffer_with_target(cls, buffer, offset=0, target=None):
+        x = cls.from_buffer(buffer)
+        x.target = target
+        return x
+
+
+class RemotePtr(RemoteValue):
+    @property
+    def raw_value(self):
+        return ctypes.cast(self, ctypes.c_void_p).value
+
+
+class RemoteCCharP(RemotePtr, ctypes.c_char_p):
+    @property
+    def value(self):
+        base = self.raw_value
+        res = []
+        for i in itertools.count():
+            x = self.target.read_memory(base + (i * 0x100), 0x100)
+            if "\x00" in x:
+                res.append(x.split("\x00", 1)[0])
+                break
+            res.append(x)
+        return "".join(res)
+
+
+class RemoteWCharP(RemotePtr, ctypes.c_char_p):
+    @property
+    def value(self):
+        base = self.raw_value
+        res = []
+        for i in itertools.count():
+            x = self.target.read_memory(base + (i * 0x100), 0x100)
+            utf16_chars = ["".join(c) for c in zip(*[iter(x)] * 2)]
+            if "\x00\x00" in utf16_chars:
+                res.extend(utf16_chars[:utf16_chars.index("\x00\x00")])
+                break
+            res.extend(x)
+        return "".join(res).decode('utf16')
+
+
+class RemoteStructurePointer(RemotePtr, ctypes.c_void_p):
+    @classmethod
+    def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None):
+        x = cls.from_buffer(buffer)
+        x.target = target
+        x.real_pointer_type = ptr_type
+        return x
+
+    @property
+    def contents(self):
+        remote_pointed_type = RemoteStructure.from_structure(self.real_pointer_type._type_)
+        return remote_pointed_type(self.raw_value, self.target)
+
+    def __repr__(self):
+        return "<RemoteStructurePointer to {0}>".format(self.real_pointer_type._type_.__name__)
+
+
+def create_remote_array(subtype, len):
+
+    class RemoteArray(_ctypes.Array):
+        _length_ = len
+        _type_ = subtype
+
+        def __init__(self, addr, target):
+            self._base_addr = addr
+            self.target = target
+
+        def __getitem__(self, slice):
+            if not isinstance(slice, (int, long)):
+                raise NotImplementedError("RemoteArray slice __getitem__")
+            if slice >= len:
+                raise IndexError("Access to {0} for a RemoteArray of size {1}".format(slice, len))
+            item_addr = self._base_addr + (ctypes.sizeof(subtype) * slice)
+
+            # TODO: do better ?
+            class TST(ctypes.Structure):
+                _fields_ = [("TST", subtype)]
+            return RemoteStructure.from_structure(TST)(item_addr, target=self.target).TST
+    return RemoteArray
+
+
+# 64bits pointers
+
+class RemotePtr64(RemoteValue):
+    def __init__(self, value, target):
+        self.target = target
+        super(RemotePtr64, self).__init__(value)
+
+    @property
+    def raw_value(self):
+        # Bypass our own 'value' implementation
+        # Even if we are a subclass of c_ulonglong
+        my_addr = ctypes.addressof(self)
+        return ctypes.c_ulonglong.from_address(my_addr).value
+
+
+class Remote_c_void_p64(RemotePtr64, c_void_p64):
+    pass
+
+
+# base explanation:
+# RemotePtr64 for the good `raw_value` implem
+# RemoteCCharP for the good `value` implem
+# c_char_p64 for the good _type_ (ctypes size)
+class Remote_c_char_p64(c_char_p64, RemotePtr64, RemoteCCharP):
+    def __repr__(self):
+        return "<Remote_c_char_p64({0})>".format(self.raw_value)
+
+
+class Remote_w_char_p64(c_wchar_p64, RemotePtr64, RemoteWCharP):
+    def __repr__(self):
+        return "<Remote_c_wchar_p64({0})>".format(self.raw_value)
+
+
+class RemoteStructurePointer64(Remote_c_void_p64):
+    @property
+    def raw_value(self):
+        return self.value
+
+    @classmethod
+    def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None):
+        x = cls.from_buffer(buffer)
+        x.target = target
+        x.real_pointer_type = ptr_type
+        return x
+
+    @property
+    def contents(self):
+        remote_pointed_type = transform_type_to_remote64bits(self.real_pointer_type._sub_ctypes_)
+        return remote_pointed_type(self.raw_value, self.target)
+
+
+type_32_64_translation_table = {
+    ctypes.c_void_p: Remote_c_void_p64,
+    ctypes.c_char_p: Remote_c_char_p64,
+    ctypes.c_wchar_p: Remote_w_char_p64,
+}
+
+
+# 32bits pointers
+
+class RemotePtr32(RemoteValue):
+    def __init__(self, value, target):
+        self.target = target
+        super(RemotePtr32, self).__init__(value)
+
+    @property
+    def raw_value(self):
+        # Bypass our own 'value' implementation
+        # Even if we are a subclass of c_ulonglong
+        my_addr = ctypes.addressof(self)
+        return ctypes.c_ulong.from_address(my_addr).value
+
+
+class Remote_c_void_p32(RemotePtr32, c_void_p32):
+    pass
+
+
+# base explanation:
+# RemotePtr64 for the good `raw_value` implem
+# RemoteCCharP for the good `value` implem
+# c_char_p64 for the good _type_ (ctypes size)
+class Remote_c_char_p32(c_char_p32, RemotePtr32, RemoteCCharP):
+    def __repr__(self):
+        return "<Remote_c_char_p32({0})>".format(self.raw_value)
+
+
+class Remote_w_char_p32(c_wchar_p32, RemotePtr32, RemoteWCharP):
+    def __repr__(self):
+        return "<Remote_c_wchar_p32({0})>".format(self.raw_value)
+
+
+class RemoteStructurePointer32(Remote_c_void_p32):
+    @property
+    def raw_value(self):
+        return self.value
+
+    @classmethod
+    def from_buffer_with_target_and_ptr_type(cls, buffer, offset=0, target=None, ptr_type=None):
+        x = cls.from_buffer(buffer)
+        x.target = target
+        x.real_pointer_type = ptr_type
+        return x
+
+    @property
+    def contents(self):
+        remote_pointed_type = transform_type_to_remote32bits(self.real_pointer_type._sub_ctypes_)
+        return remote_pointed_type(self.raw_value, self.target)
+
+
+type_64_32_translation_table = {
+    ctypes.c_void_p: Remote_c_void_p32,
+    ctypes.c_char_p: Remote_c_char_p32,
+    ctypes.c_wchar_p: Remote_w_char_p32,
+}
+
+
+class RemoteStructureUnion(object):
+    """Target is a process object"""
+    _reserved_name = ["_target", "_fields_", "_fields_dict_", "_base_addr", "_get_field_by_name",
+                      "_get_field_descrptor_by_name", "_handle_field_getattr", "_field_type_to_remote_type",
+                      "__getattribute__", "_fields_"]
+
+    _field_type_to_remote_type = {
+        ctypes.c_char_p: RemoteCCharP,
+        ctypes.c_wchar_p: RemoteWCharP,
+        Remote_c_void_p64: Remote_c_void_p64,
+        Remote_c_char_p64: Remote_c_char_p64,
+        Remote_w_char_p64: Remote_w_char_p64,
+        Remote_c_void_p32: Remote_c_void_p32,
+        Remote_c_char_p32: Remote_c_char_p32,
+        Remote_w_char_p32: Remote_w_char_p32
+    }
+
+    def __init__(self, base_addr, target):
+        self._target = target
+        self._base_addr = base_addr
+        self._fields_dict_ = dict(self._fields_)
+
+    def _get_field_by_name(self, fieldname):
+        try:
+            return self._fields_dict_[fieldname]
+        except KeyError:
+            raise AttributeError(fieldname + "is not a field of {0}".format(type(self)))
+
+    def _get_field_descrptor_by_name(self, fieldname):
+        return getattr(type(self), fieldname)  # ctypes metaclass fill this for us
+
+    def _handle_field_getattr(self, ftype, fosset, fsize):
+        s = self._target.read_memory(self._base_addr + fosset, fsize)
+        if ftype in self._field_type_to_remote_type:
+            return self._field_type_to_remote_type[ftype].from_buffer_with_target(bytearray(s), target=self._target).value
+        if issubclass(ftype, _ctypes._Pointer):  # Pointer
+            return RemoteStructurePointer.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
+        if issubclass(ftype, RemotePtr64):  # Pointer to remote64 bits process
+            return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
+        if issubclass(ftype, RemotePtr32):  # Pointer to remote32 bits process
+            return RemoteStructurePointer32.from_buffer_with_target_and_ptr_type(bytearray(s), target=self._target, ptr_type=ftype)
+        if issubclass(ftype, RemoteStructureUnion):  # Structure|Union already transfomed in remote
+            return ftype(self._base_addr + fosset, self._target)
+        if issubclass(ftype, ctypes.Structure):  # Structure that must be transfomed
+            return RemoteStructure.from_structure(ftype)(self._base_addr + fosset, self._target)
+        if issubclass(ftype, ctypes.Union):  # Union that must be transfomed
+            return RemoteUnion.from_structure(ftype)(self._base_addr + fosset, self._target)
+        if issubclass(ftype, _ctypes.Array):  # Arrays
+            return create_remote_array(ftype._type_, ftype._length_)(self._base_addr + fosset, self._target)
+        # Normal types
+        # Follow the ctypes usage: if it's not directly inherited from _SimpleCData
+        # We do not apply the .value
+        # Seems weird but it's mandatory AND useful :D (in pe_parse)
+        if _SimpleCData not in ftype.__bases__:
+            return ftype.from_buffer(bytearray(s))
+        return ftype.from_buffer(bytearray(s)).value
+
+    def __getattribute__(self, fieldname):
+        if fieldname in type(self)._reserved_name:  # Prevent recursion !
+            return super(RemoteStructureUnion, self).__getattribute__(fieldname)
+        try:
+            t = self._get_field_by_name(fieldname)
+        except AttributeError:  # Not a real attribute
+            return super(RemoteStructureUnion, self).__getattribute__(fieldname)
+        descr = self._get_field_descrptor_by_name(fieldname)
+        return self._handle_field_getattr(t, descr.offset, descr.size)
+
+    @classmethod
+    def from_structure(cls, structcls):
+        class MyStruct(cls, structcls):  # inherit of structcls to keep property (see winobject.LoadedModule)
+            _fields_ = structcls._fields_
+
+        MyStruct.__name__ = "Remote" + structcls.__name__
+        return MyStruct
+
+    @classmethod
+    def from_fields(cls, fields, base_cls=None):
+        bases = [cls]
+        if base_cls:
+            bases.append(base_cls)
+        # inherit of structcls to keep property (see winobject.LoadedModule)
+        RemoteStruct = type("RemoteStruct", tuple(bases), {"_fields_": fields})
+        if base_cls:
+            RemoteStruct.__name__ = "Remote" + base_cls.__name__
+        return RemoteStruct
+
+
+class RemoteStructure(RemoteStructureUnion, ctypes.Structure):
+    pass
+
+
+class RemoteUnion(RemoteStructureUnion, ctypes.Union):
+    pass
+
+
+remote_struct = RemoteStructure.from_structure
+
+# ctypes 32 -> 64 methods
+def MakePtr64(type):
+    class PointerToStruct64(Remote_c_void_p64):
+        _sub_ctypes_ = (type)
+
+        @property
+        def contents(self):
+            return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(self), target=self.target, ptr_type=self).contents
+
+        def __repr__(self):
+            return "<RemotePtr64 to struct {0}>".format(type.__name__)
+    return PointerToStruct64
+
+def transform_structure_to_remote64bits(structcls):
+    """Create a remote structure for a 64bits target process"""
+    new_fields = []
+    for fname, ftype in structcls._fields_:
+        ftype = transform_type_to_remote64bits(ftype)
+        new_fields.append((fname, ftype))
+    return RemoteStructure.from_fields(new_fields, base_cls=structcls)
+
+def transform_union_to_remote64bits(structcls):
+    """Create a remote union for a 64bits target process"""
+    new_fields = []
+    for fname, ftype in structcls._fields_:
+        ftype = transform_type_to_remote64bits(ftype)
+        new_fields.append((fname, ftype))
+    return RemoteUnion.from_fields(new_fields, base_cls=structcls)
+
+
[docs]def transform_type_to_remote64bits(ftype): + if is_pointer_type(ftype): + return MakePtr64(ftype._type_) + if is_array_type(ftype): + return create_remote_array(transform_type_to_remote64bits(ftype._type_), ftype._length_) + if is_structure_type(ftype): + return transform_structure_to_remote64bits(ftype) + if is_union_type(ftype): + return transform_union_to_remote64bits(ftype) + # Normal types + return type_32_64_translation_table.get(ftype, ftype)
+ + +# ctypes 64 -> 32 methods +def MakePtr32(type): + class PointerToStruct32(Remote_c_void_p32): + _sub_ctypes_ = (type) + + # Not sur about this code.. + # Logic problem: why do I have PointerToStruct32 and RemoteStructurePointer32... ? + @property + def contents(self): + return RemoteStructurePointer32.from_buffer_with_target_and_ptr_type(bytearray(self), target=self.target, ptr_type=self).contents + + def __repr__(self): + return "<RemotePtr32 to struct {0}>".format(type.__name__) + + return PointerToStruct32 + +def transform_structure_to_remote32bits(structcls): + """Create a remote structure for a 32bits target process""" + new_fields = [] + for fname, ftype in structcls._fields_: + ftype = transform_type_to_remote32bits(ftype) + new_fields.append((fname, ftype)) + return RemoteStructure.from_fields(new_fields, base_cls=structcls) + +def transform_union_to_remote32bits(structcls): + """Create a remote union for a 32bits target process""" + new_fields = [] + for fname, ftype in structcls._fields_: + ftype = transform_type_to_remote32bits(ftype) + new_fields.append((fname, ftype)) + return RemoteUnion.from_fields(new_fields, base_cls=structcls) + +
[docs]def transform_type_to_remote32bits(ftype): + if issubclass(ftype, RemoteStructureUnion): + return ftype + if is_pointer_type(ftype): + return MakePtr32(ftype._type_) + if is_array_type(ftype): + return create_remote_array(transform_type_to_remote32bits(ftype._type_), ftype._length_) + if is_structure_type(ftype): + return transform_structure_to_remote32bits(ftype) + if is_union_type(ftype): + return transform_union_to_remote32bits(ftype) + # Normal types + return type_64_32_translation_table.get(ftype, ftype)
+ +if ctypes.sizeof(ctypes.c_void_p) == 4: + transform_type_to_remote = transform_type_to_remote32bits +if ctypes.sizeof(ctypes.c_void_p) == 8: + transform_type_to_remote = transform_type_to_remote64bits +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/syswow64.html b/docs/build/html/_modules/windows/syswow64.html new file mode 100644 index 0000000..b108e1b --- /dev/null +++ b/docs/build/html/_modules/windows/syswow64.html @@ -0,0 +1,385 @@ + + + + + + + + windows.syswow64 — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.syswow64

+import struct
+import ctypes
+from ctypes import byref
+import codecs
+import functools
+
+import windows
+import windows.native_exec.simple_x86 as x86
+import windows.native_exec.simple_x64 as x64
+from generated_def.winstructs import *
+from windows.winobject import process
+from windows import winproxy
+from winproxy import NeededParameter, NtdllProxy, error_ntstatus
+
+# Special code for syswow64 process
+CS_32bits = 0x23
+CS_64bits = 0x33
+
+
+def generate_64bits_execution_stub_from_syswow(x64shellcode):
+    """shellcode must NOT end by a ret"""
+    current_process = windows.current_process
+    if not current_process.is_wow_64:
+        raise ValueError("Calling generate_64bits_execution_stub_from_syswow from non-syswow process")
+
+    transition64 = x64.MultipleInstr()
+    transition64 += x64.Call(":TOEXEC")
+    transition64 += x64.Mov("RDX", "RAX")
+    transition64 += x64.Shr("RDX", 32)
+    transition64 += x64.Retf32()  # 32 bits return addr
+    transition64 += x64.Label(":TOEXEC")
+    x64shellcodeaddr = windows.current_process.allocator.write_code(transition64.get_code() + x64shellcode)
+
+    transition =     x86.MultipleInstr()
+    transition +=    x86.Call(CS_64bits, x64shellcodeaddr)
+    transition +=    x86.Ret()
+
+    stubaddr = windows.current_process.allocator.write_code(transition.get_code())
+    exec_stub = ctypes.CFUNCTYPE(ULONG64)(stubaddr)
+    return exec_stub
+
+
[docs]def execute_64bits_code_from_syswow(x64shellcode): + return generate_64bits_execution_stub_from_syswow(x64shellcode)()
+ +
[docs]def generate_syswow64_call(target): + nb_args = len(target.prototype._argtypes_) + target_addr = get_syswow_ntdll_exports()[target.__name__] + argument_buffer_len = (nb_args * 8) + argument_buffer = windows.current_process.allocator.reserve_size(argument_buffer_len) + alignement_information = windows.current_process.allocator.reserve_size(8) + + nb_args_on_stack = max(nb_args - 4, 0) + + code_64b = x64.MultipleInstr() + # Save registers + + code_64b += x64.Push('RBX') + code_64b += x64.Push('RCX') + code_64b += x64.Push('RDX') + code_64b += x64.Push('RSI') + code_64b += x64.Push('RDI') + code_64b += x64.Push('R8') + code_64b += x64.Push('R9') + code_64b += x64.Push('R10') + code_64b += x64.Push('R11') + code_64b += x64.Push('R12') + code_64b += x64.Push('R13') + + # Alignment stuff :) + code_64b += x64.Mov('RCX', 'RSP') + code_64b += x64.And('RCX', 0x0f) + code_64b += x64.Mov(x64.deref(alignement_information), 'RCX') + code_64b += x64.Sub('RSP', 'RCX') + # retrieve argument from the argument buffer + if nb_args >= 1: + code_64b += x64.Mov('RCX', x64.create_displacement(disp=argument_buffer)) + if nb_args >= 2: + code_64b += x64.Mov('RDX', x64.create_displacement(disp=argument_buffer + (8 * 1))) + if nb_args >= 3: + code_64b += x64.Mov('R8', x64.create_displacement(disp=argument_buffer + (8 * 2))) + if nb_args >= 4: + code_64b += x64.Mov('R9', x64.create_displacement(disp=argument_buffer + (8 * 3))) + for i in range(nb_args_on_stack): + code_64b += x64.Mov('RAX', x64.create_displacement(disp=argument_buffer + 8 * (nb_args - 1 - i))) + code_64b += x64.Push('RAX') + # reserve space for register (calling convention) + code_64b += x64.Push('R9') + code_64b += x64.Push('R8') + code_64b += x64.Push('RDX') + code_64b += x64.Push('RCX') + # Call + code_64b += x64.Mov('R13', target_addr) + code_64b += x64.Call('R13') + # Realign stack :) + code_64b += x64.Add('RSP', x64.deref(alignement_information)) + # Clean stack + code_64b += x64.Add('RSP', (4 + nb_args_on_stack) * 8) + code_64b += x64.Pop('R13') + code_64b += x64.Pop('R12') + code_64b += x64.Pop('R11') + code_64b += x64.Pop('R10') + code_64b += x64.Pop('R9') + code_64b += x64.Pop('R8') + code_64b += x64.Pop('RDI') + code_64b += x64.Pop('RSI') + code_64b += x64.Pop('RDX') + code_64b += x64.Pop('RCX') + code_64b += x64.Pop('RBX') + code_64b += x64.Ret() + return try_generate_stub_target(code_64b.get_code(), argument_buffer, target)
+ + +
[docs]def try_generate_stub_target(shellcode, argument_buffer, target): + """shellcode must NOT end by a ret""" + if not windows.current_process.is_wow_64: + raise ValueError("Calling execute_64bits_code_from_syswow from non-syswow process") + native_caller = generate_64bits_execution_stub_from_syswow(shellcode) + native_caller.errcheck = target.errcheck + # Generate the wrapper function that fill the argument_buffer + expected_arguments_number = len(target.prototype._argtypes_) + def wrapper(*args): + if len(args) != expected_arguments_number: + raise ValueError("{0} syswow accept {1} args ({2} given)".format(target.__name__, expected_arguments_number, len(args))) + # Transform args (ctypes byref possibly) to int + writable_args = [] + for i, value in enumerate(args): + if not isinstance(value, (int, long)): + try: + value = ctypes.cast(value, ctypes.c_void_p).value + except ctypes.ArgumentError as e: + raise ctypes.ArgumentError("Argument {0}: wrong type <{1}>".format(i, type(value).__name__)) + writable_args.append(value) + # Build buffer + buffer = struct.pack("<" + "Q" * len(writable_args), *writable_args) + ctypes.memmove(argument_buffer, buffer, len(buffer)) + return native_caller() + wrapper.__name__ = "{0}<syswow64>".format(target.__name__,) + wrapper.__doc__ = "This is a wrapper to {0} in 64b mode, it accept <{1}> args".format(target.__name__, expected_arguments_number) + return wrapper
+ + +def get_current_process_syswow_peb_addr(): + get_peb_64_code = x64.assemble("mov rax, gs:[0x60]; ret") + return execute_64bits_code_from_syswow(get_peb_64_code) + +def get_current_process_syswow_peb(): + current_process = windows.current_process + + class CurrentProcessReadSyswow(process.Process): + bitness = 64 + def _get_handle(self): + return winproxy.OpenProcess(dwProcessId=current_process.pid) + + def read_memory(self, addr, size): + buffer_addr = ctypes.create_string_buffer(size) + winproxy.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size) + return buffer_addr[:] + peb_addr = get_current_process_syswow_peb_addr() + return windows.winobject.process.RemotePEB64(peb_addr, CurrentProcessReadSyswow()) + + +class ReadSyswow64Process(process.Process): + def __init__(self, target): + self.target = target + self._bitness = target.bitness + + def _get_handle(self): + return self.target.handle + + def read_memory(self, addr, size): + buffer_addr = ctypes.create_string_buffer(size) + winproxy.NtWow64ReadVirtualMemory64(self.target.handle, addr, buffer_addr, size) + return buffer_addr[:] + + #read_string = process.Process.read_string + + +def get_syswow_ntdll_exports(): + if get_syswow_ntdll_exports.value is not None: + return get_syswow_ntdll_exports.value + peb64 = get_current_process_syswow_peb() + ntdll64 = [m for m in peb64.modules if m.name == "ntdll.dll"] + if not ntdll64: + raise ValueError("Could not find ntdll.dll in syswow peb") + ntdll64 = ntdll64[0] + exports = ntdll64.pe.exports + get_syswow_ntdll_exports.value = exports + return exports +get_syswow_ntdll_exports.value = None + + +
[docs]class Syswow64ApiProxy(object): + """Create a python wrapper around a function""" + def __init__(self, winproxy_function): + self.winproxy_function = winproxy_function + self.raw_call = None + if winproxy_function is not None: + self.params_name = [param[1] for param in winproxy_function.params] + + + + + def __call__(self, python_proxy): + if not windows.winproxy.is_implemented(self.winproxy_function): + return None + + def force_resolution(): + if self.raw_call: + return True + try: + self.raw_call = generate_syswow64_call(self.winproxy_function) + except KeyError: + raise windows.winproxy.ExportNotFound(self.winproxy_function.__name__, "SysWow[ntdll64]") + + + def perform_call(*args): + if len(self.params_name) != len(args): + print("ERROR:") + print("Expected params: {0}".format(self.params_name)) + print("Just Got params: {0}".format(args)) + raise ValueError("I do not have all parameters: how is that possible ?") + for param_name, param_value in zip(self.params_name, args): + if param_value is NeededParameter: + raise TypeError("{0}: Missing Mandatory parameter <{1}>".format(self.winproxy_function.__name__, param_name)) + + if self.raw_call is None: + force_resolution() + return self.raw_call(*args) + setattr(python_proxy, "ctypes_function", perform_call) + setattr(python_proxy, "force_resolution", force_resolution) + return python_proxy
+ + + +@Syswow64ApiProxy(winproxy.NtCreateThreadEx) +
[docs]def NtCreateThreadEx_32_to_64(ThreadHandle=None, DesiredAccess=0x1fffff, ObjectAttributes=0, ProcessHandle=NeededParameter, lpStartAddress=NeededParameter, lpParameter=NeededParameter, CreateSuspended=0, dwStackSize=0, Unknown1=0, Unknown2=0, Unknown3=0): + if ThreadHandle is None: + ThreadHandle = byref(HANDLE()) + return NtCreateThreadEx_32_to_64.ctypes_function(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, lpStartAddress, lpParameter, CreateSuspended, dwStackSize, Unknown1, Unknown2, Unknown3)
+ + +ProcessBasicInformation = 0 +@Syswow64ApiProxy(winproxy.NtQueryInformationProcess) +
[docs]def NtQueryInformationProcess_32_to_64(ProcessHandle, ProcessInformationClass=ProcessBasicInformation, ProcessInformation=NeededParameter, ProcessInformationLength=0, ReturnLength=None): + if ProcessInformation is not None and ProcessInformationLength == 0: + ProcessInformationLength = ctypes.sizeof(ProcessInformation) + if type(ProcessInformation) == PROCESS_BASIC_INFORMATION: + ProcessInformation = byref(ProcessInformation) + if ReturnLength is None: + ReturnLength = byref(ULONG()) + return NtQueryInformationProcess_32_to_64.ctypes_function(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength)
+ + +@Syswow64ApiProxy(winproxy.NtQueryInformationThread) +
[docs]def NtQueryInformationThread_32_to_64(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength=0, ReturnLength=None): + if ReturnLength is None: + ReturnLength = byref(ULONG()) + if ThreadInformation is not None and ThreadInformationLength == 0: + ThreadInformationLength = ctypes.sizeof(ThreadInformation) + return NtQueryInformationThread_32_to_64.ctypes_function(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength, ReturnLength)
+ + + +@Syswow64ApiProxy(winproxy.NtQueryVirtualMemory) +
[docs]def NtQueryVirtualMemory_32_to_64(ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation=NeededParameter, MemoryInformationLength=0, ReturnLength=None): + if ReturnLength is None: + ReturnLength = byref(ULONG()) + if MemoryInformation is not None and MemoryInformationLength == 0: + MemoryInformationLength = ctypes.sizeof(MemoryInformation) + if isinstance(MemoryInformation, ctypes.Structure): + MemoryInformation = byref(MemoryInformation) + return NtQueryVirtualMemory_32_to_64.ctypes_function(ProcessHandle, BaseAddress, MemoryInformationClass, MemoryInformation, MemoryInformationLength, ReturnLength)
+ + +@Syswow64ApiProxy(winproxy.NtProtectVirtualMemory) +def NtProtectVirtualMemory_32_to_64(ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection=None): + if OldAccessProtection is None: + XOldAccessProtection = DWORD() + OldAccessProtection = ctypes.addressof(XOldAccessProtection) + return NtProtectVirtualMemory_32_to_64.ctypes_function(ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection) + + + +@Syswow64ApiProxy(winproxy.NtGetContextThread) +
[docs]def NtGetContextThread_32_to_64(hThread, lpContext): + if type(lpContext) == windows.winobject.exception.ECONTEXT64: + lpContext = byref(lpContext) + return NtGetContextThread_32_to_64.ctypes_function(hThread, lpContext)
+ +@Syswow64ApiProxy(winproxy.LdrLoadDll) +
[docs]def LdrLoadDll_32_to_64(PathToFile, Flags, ModuleFileName, ModuleHandle): + return LdrLoadDll_32_to_64.ctypes_function(PathToFile, Flags, ModuleFileName, ModuleHandle)
+ +@Syswow64ApiProxy(winproxy.NtSetContextThread) +
[docs]def NtSetContextThread_32_to_64(hThread, lpContext): + return NtSetContextThread_32_to_64.ctypes_function(hThread, lpContext)
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/utils/winutils.html b/docs/build/html/_modules/windows/utils/winutils.html new file mode 100644 index 0000000..051317d --- /dev/null +++ b/docs/build/html/_modules/windows/utils/winutils.html @@ -0,0 +1,354 @@ + + + + + + + + windows.utils.winutils — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.utils.winutils

+import ctypes
+import msvcrt
+import os
+import sys
+import code
+import datetime
+
+import windows
+from .. import winproxy
+from ..generated_def import windef
+from ..generated_def.winstructs import *
+
+
+# Function resolution !
+def get_func_addr(dll_name, func_name):
+        # Load the DLL
+        ctypes.WinDLL(dll_name)
+        modules = windows.current_process.peb.modules
+        if not dll_name.lower().endswith(".dll"):
+            dll_name += ".dll"
+        mod = [x for x in modules if x.name == dll_name][0]
+        return mod.pe.exports[func_name]
+
+
+def get_remote_func_addr(target, dll_name, func_name):
+        name_modules = [m for m in target.peb.modules if m.name == dll_name]
+        if not len(name_modules):
+            raise ValueError("Module <{0}> not loaded in target <{1}>".format(dll_name, target))
+        mod = name_modules[0]
+        return mod.pe.exports[func_name]
+
+
+def is_wow_64(hProcess):
+    try:
+        fnIsWow64Process = get_func_addr("kernel32.dll", "IsWow64Process")
+    except winproxy.Kernel32Error:
+        return False
+    IsWow64Process = ctypes.WINFUNCTYPE(BOOL, HANDLE, ctypes.POINTER(BOOL))(fnIsWow64Process)
+    Wow64Process = BOOL()
+    res = IsWow64Process(hProcess, ctypes.byref(Wow64Process))
+    if res:
+        return bool(Wow64Process)
+    raise ctypes.WinError()
+
+
+
[docs]def create_file_from_handle(handle, mode="r"): + """Return a Python :class:`file` around a ``Windows`` HANDLE""" + fd = msvcrt.open_osfhandle(handle, os.O_TEXT) + return os.fdopen(fd, mode, 0)
+ + +
[docs]def get_handle_from_file(f): + """Get the ``Windows`` HANDLE of a python :class:`file`""" + return msvcrt.get_osfhandle(f.fileno())
+ + +
[docs]def create_console(): + """Create a new console displaying STDOUT. + Useful in injection of GUI process""" + winproxy.AllocConsole() + stdout_handle = winproxy.GetStdHandle(windef.STD_OUTPUT_HANDLE) + console_stdout = create_file_from_handle(stdout_handle, "w") + sys.stdout = console_stdout + + stdin_handle = winproxy.GetStdHandle(windef.STD_INPUT_HANDLE) + console_stdin = create_file_from_handle(stdin_handle, "r+") + sys.stdin = console_stdin + + stderr_handle = winproxy.GetStdHandle(windef.STD_ERROR_HANDLE) + console_stderr = create_file_from_handle(stderr_handle, "w") + sys.stderr = console_stderr
+ + +
[docs]def create_process(path, args=None, dwCreationFlags=0, show_windows=True): + """A convenient wrapper arround :func:`windows.winproxy.CreateProcessA`""" + proc_info = PROCESS_INFORMATION() + lpStartupInfo = None + if show_windows: + StartupInfo = STARTUPINFOA() + StartupInfo.cb = ctypes.sizeof(StartupInfo) + StartupInfo.dwFlags = 0 + lpStartupInfo = ctypes.byref(StartupInfo) + lpCommandLine = None + if args: + lpCommandLine = (" ".join([str(a) for a in args])) + windows.winproxy.CreateProcessA(path, lpCommandLine=lpCommandLine, dwCreationFlags=dwCreationFlags, lpProcessInformation=ctypes.byref(proc_info), lpStartupInfo=lpStartupInfo) + return windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)
+ + +
[docs]def enable_privilege(lpszPrivilege, bEnablePrivilege): + """ + Enable or disable a privilege:: + + enable_privilege(SE_DEBUG_NAME, True) + """ + tp = TOKEN_PRIVILEGES() + luid = LUID() + hToken = HANDLE() + + winproxy.OpenProcessToken(winproxy.GetCurrentProcess(), TOKEN_ALL_ACCESS, byref(hToken)) + winproxy.LookupPrivilegeValueA(None, lpszPrivilege, byref(luid)) + tp.PrivilegeCount = 1 + tp.Privileges[0].Luid = luid + if bEnablePrivilege: + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED + else: + tp.Privileges[0].Attributes = 0 + winproxy.AdjustTokenPrivileges(hToken, False, byref(tp), sizeof(TOKEN_PRIVILEGES)) + winproxy.CloseHandle(hToken) + if winproxy.GetLastError() == windef.ERROR_NOT_ALL_ASSIGNED: + raise ValueError("Failed to get privilege {0}".format(lpszPrivilege)) + return True
+ + +
[docs]def check_is_elevated(): + """Return ``True`` if process is Admin""" + hToken = HANDLE() + elevation = TOKEN_ELEVATION() + cbsize = DWORD() + + winproxy.OpenProcessToken(winproxy.GetCurrentProcess(), TOKEN_ALL_ACCESS, byref(hToken)) + winproxy.GetTokenInformation(hToken, TokenElevation, byref(elevation), sizeof(elevation), byref(cbsize)) + winproxy.CloseHandle(hToken) + return elevation.TokenIsElevated
+ + +
[docs]def check_debug(): + """Check that kernel is in debug mode (beware of NOUMEX): + + https://msdn.microsoft.com/en-us/library/windows/hardware/ff556253(v=vs.85).aspx#_______noumex______ + """ + hkresult = HKEY() + cbsize = DWORD(1024) + bufferres = (c_char * cbsize.value)() + + winproxy.RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control", 0, KEY_READ, byref(hkresult)) + winproxy.RegGetValueA(hkresult, None, "SystemStartOptions", RRF_RT_REG_SZ, None, byref(bufferres), byref(cbsize)) + winproxy.RegCloseKey(hkresult) + + control = bufferres[:] + if "DEBUG" not in control: + # print "[-] Enable debug boot!" + # print "> bcdedit /debug on" + return False + if "DEBUG=NOUMEX" not in control: + pass + # print "[*] Warning noumex not set!" + # print "> bcdedit /set noumex on" + return True
+ + +def datetime_from_filetime(filetime): + """return a :class:`datetime.datetime` from a ``windows`` FILETIME int""" + return datetime.datetime(1601,1,1) + datetime.timedelta(microseconds=filetime / 10) + +def filetime_from_datetime(dtime): + """Return the FILETIME value from a :class:`datetime.datetime` in a python :class:`int`""" + return int((dtime - datetime.datetime(1601,1,1)).total_seconds() * 1000) * 10000 + + +class FixedInteractiveConsole(code.InteractiveConsole): + def raw_input(self, prompt=">>>"): + sys.stdout.write(prompt) + return raw_input("") + + +
[docs]def pop_shell(): + """Pop a console with an InterativeConsole""" + create_console() + FixedInteractiveConsole(locals()).interact()
+ + +def get_kernel_modules(): + cbsize = DWORD() + + winproxy.NtQuerySystemInformation(SystemModuleInformation, None, 0, byref(cbsize)) + raw_buffer = (cbsize.value * c_char)() + buffer = SYSTEM_MODULE_INFORMATION.from_address(ctypes.addressof(raw_buffer)) + winproxy.NtQuerySystemInformation(SystemModuleInformation, byref(raw_buffer), sizeof(raw_buffer), byref(cbsize)) + modules = (SYSTEM_MODULE * buffer.ModulesCount).from_address(addressof(buffer) + SYSTEM_MODULE_INFORMATION.Modules.offset) + return list(modules) + + +# String stuff +def ntstatus(code): + return windows.generated_def.ntstatus.NtStatusException(code) + + +def get_long_path(path): + """Return the long path form for ``path`` + + :returns: :class:`str` + """ + size = 0x1000 + buffer = ctypes.c_buffer(size) + rsize = winproxy.GetLongPathNameA(path, buffer, size) + return buffer[:rsize] + +def get_short_path(path): + """Return the short path form for ``path`` + + :returns: :class:`str` + """ + size = 0x1000 + buffer = ctypes.c_buffer(size) + rsize = winproxy.GetShortPathNameA(path, buffer, size) + return buffer[:rsize] + +def get_shared_mapping(name, size=0x1000): + # TODO: real code + h = windows.winproxy.CreateFileMappingA(INVALID_HANDLE_VALUE, dwMaximumSizeLow=size, lpName=name) + addr = windows.winproxy.MapViewOfFile(h, dwNumberOfBytesToMap=size) + return addr + +#def mapfile(file): +# fhandle = get_handle_from_file(file) +# h = windows.winproxy.CreateFileMappingA(fhandle, None, PAGE_READONLY, 0, 1, None) +# addr = windows.winproxy.MapViewOfFile(h, dwDesiredAccess=FILE_MAP_READ, dwNumberOfBytesToMap=1) +# return addr + +
[docs]class VirtualProtected(object): + """ + A context manager usable like `VirtualProtect` that will restore the old protection at exit :: + + with utils.VirtualProtected(IATentry.addr, ctypes.sizeof(PVOID), windef.PAGE_EXECUTE_READWRITE): + IATentry.value = 0x42424242 + """ + def __init__(self, addr, size, new_protect): + if (addr % 0x1000): + addr = addr - addr % 0x1000 + self.addr = addr + self.size = size + self.new_protect = new_protect + + def __enter__(self): + self.old_protect = DWORD() + winproxy.VirtualProtect(self.addr, self.size, self.new_protect, ctypes.byref(self.old_protect)) + return self + + def __exit__(self, exc_type, exc_value, traceback): + winproxy.VirtualProtect(self.addr, self.size, self.old_protect.value, ctypes.byref(self.old_protect)) + return False
+ + +
[docs]class DisableWow64FsRedirection(object): + """ + A context manager that disable the SysWow64 Filesystem Redirection :: + + if is_process_32_bits: + def pop_calc_64(): + with windows.utils.DisableWow64FsRedirection(): + return windows.utils.create_process(r"C:\Windows\system32\calc.exe", True) + """ + def __enter__(self): + if windows.current_process.bitness == 64: + return self + self.OldValue = PVOID() + winproxy.Wow64DisableWow64FsRedirection(ctypes.byref(self.OldValue)) + return self + + def __exit__(self, exc_type, exc_value, traceback): + if windows.current_process.bitness == 64: + return False + winproxy.Wow64RevertWow64FsRedirection(self.OldValue) + return False
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/exception.html b/docs/build/html/_modules/windows/winobject/exception.html new file mode 100644 index 0000000..697d2c9 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/exception.html @@ -0,0 +1,464 @@ + + + + + + + + windows.winobject.exception — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.exception

+import ctypes
+import windows
+from windows.generated_def.winstructs import *
+import windows.generated_def.windef as windef
+
+EXCEPTION_CONTINUE_SEARCH = (0x0)
+EXCEPTION_CONTINUE_EXECUTION = (0xffffffff)
+
+exception_type = [
+    "EXCEPTION_ACCESS_VIOLATION",
+    "EXCEPTION_DATATYPE_MISALIGNMENT",
+    "EXCEPTION_BREAKPOINT",
+    "EXCEPTION_SINGLE_STEP",
+    "EXCEPTION_ARRAY_BOUNDS_EXCEEDED",
+    "EXCEPTION_FLT_DENORMAL_OPERAND",
+    "EXCEPTION_FLT_DIVIDE_BY_ZERO",
+    "EXCEPTION_FLT_INEXACT_RESULT",
+    "EXCEPTION_FLT_INVALID_OPERATION",
+    "EXCEPTION_FLT_OVERFLOW",
+    "EXCEPTION_FLT_STACK_CHECK",
+    "EXCEPTION_FLT_UNDERFLOW",
+    "EXCEPTION_INT_DIVIDE_BY_ZERO",
+    "EXCEPTION_INT_OVERFLOW",
+    "EXCEPTION_PRIV_INSTRUCTION",
+    "EXCEPTION_IN_PAGE_ERROR",
+    "EXCEPTION_ILLEGAL_INSTRUCTION",
+    "EXCEPTION_NONCONTINUABLE_EXCEPTION",
+    "EXCEPTION_STACK_OVERFLOW",
+    "EXCEPTION_INVALID_DISPOSITION",
+    "EXCEPTION_GUARD_PAGE",
+    "EXCEPTION_INVALID_HANDLE",
+    "EXCEPTION_POSSIBLE_DEADLOCK",
+]
+
+# x -> x dict may seems strange but useful to get the Flags (with name) from the int
+# exception_name_by_value[0x80000001] -> EXCEPTION_GUARD_PAGE(0x80000001L)
+exception_name_by_value = dict([(x, x) for x in [getattr(windows.generated_def.windef, name) for name in exception_type]])
+
+class EEXCEPTION_RECORDBase(object):
+        @property
+        def ExceptionCode(self):
+            """The Exception code
+
+               :type: :class:`int`"""
+            real_code = super(EEXCEPTION_RECORDBase, self).ExceptionCode
+            return exception_name_by_value.get(real_code, windows.generated_def.windef.Flag("UNKNOW_EXCEPTION", real_code))
+
+        @property
+        def ExceptionAddress(self):
+            """The Exception Address
+
+            :type: :class:`int`"""
+            x = super(EEXCEPTION_RECORDBase, self).ExceptionAddress
+            if x is None:
+                return 0x0
+            return x
+
+
[docs]class EEXCEPTION_RECORD(EEXCEPTION_RECORDBase, EXCEPTION_RECORD): + """Enhanced exception record""" + + fields = [f[0] for f in EXCEPTION_RECORD._fields_] + """The fields of the structure"""
+ +
[docs]class EEXCEPTION_RECORD32(EEXCEPTION_RECORDBase, EXCEPTION_RECORD32): + """Enhanced exception record (32bits)""" + + fields = [f[0] for f in EXCEPTION_RECORD32._fields_] + """The fields of the structure"""
+ +
[docs]class EEXCEPTION_RECORD64(EEXCEPTION_RECORDBase, EXCEPTION_RECORD64): + """Enhanced exception record (64bits)""" + + fields = [f[0] for f in EXCEPTION_RECORD64._fields_] + """The fields of the structure"""
+ + +
[docs]class EEXCEPTION_DEBUG_INFO32(ctypes.Structure): + """Enhanced Debug info""" + _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EEXCEPTION_RECORD32}) + + fields = [f[0] for f in _fields_] + """The fields of the structure"""
+ +
[docs]class EEXCEPTION_DEBUG_INFO64(ctypes.Structure): + """Enhanced Debug info""" + _fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EEXCEPTION_RECORD64}) + + fields = [f[0] for f in _fields_] + """The fields of the structure"""
+ + +
[docs]class EEflags(ctypes.Structure): + "Flag view of the Eflags register" + _fields_ = [("CF", DWORD, 1), + ("RES_1", DWORD, 1), + ("PF", DWORD, 1), + ("RES_3", DWORD, 1), + ("AF", DWORD, 1), + ("RES_5", DWORD, 1), + ("ZF", DWORD, 1), + ("SF", DWORD, 1), + ("TF", DWORD, 1), + ("IF", DWORD, 1), + ("DF", DWORD, 1), + ("OF", DWORD, 1), + ("IOPL_1", DWORD, 1), + ("IOPL_2", DWORD, 1), + ("NT", DWORD, 1), + ("RES_15", DWORD, 1), + ("RF", DWORD, 1), + ("VM", DWORD, 1), + ("AC", DWORD, 1), + ("VIF", DWORD, 1), + ("VIP", DWORD, 1), + ("ID", DWORD, 1), + ] + + fields = [f[0] for f in _fields_] + """The fields of the structure""" + + def get_raw(self): + x = DWORD.from_address(ctypes.addressof(self)) + return x.value + + def set_raw(self, value): + x = DWORD.from_address(ctypes.addressof(self)) + x.value = value + return None + + def dump(self): + res = [] + for name in [x[0] for x in self._fields_]: + if name.startswith("RES_"): + continue + if getattr(self, name): + res.append(name) + return "|".join(res) + + def __repr__(self): + return hex(self) + + def __hex__(self): + if self.raw == 0: + return "{0}({1})".format(type(self).__name__, hex(self.raw)) + return "{0}({1}:{2})".format(type(self).__name__, hex(self.raw), self.dump()) + + raw = property(get_raw, set_raw) + """Raw value of the eflags + + :type: :class:`int` + """
+ + +
[docs]class EDr7(ctypes.Structure): + "Flag view of the DR7 register" + _fields_ = [("L0", DWORD, 1), + ("G0", DWORD, 1), + ("L1", DWORD, 1), + ("G1", DWORD, 1), + ("L2", DWORD, 1), + ("G2", DWORD, 1), + ("L3", DWORD, 1), + ("G3", DWORD, 1), + ("LE", DWORD, 1), + ("GE", DWORD, 1), + ("RES_1", DWORD, 3), + ("GD", DWORD, 1), + ("RES_1", DWORD, 2), + ("RW0", DWORD, 2), + ("LEN0", DWORD, 2), + ("RW1", DWORD, 2), + ("LEN1", DWORD, 2), + ("RW2", DWORD, 2), + ("LEN2", DWORD, 2), + ("RW3", DWORD, 2), + ("LEN3", DWORD, 2), + ] + + fields = [f[0] for f in _fields_] + """The fields of the structure"""
+ +class ECONTEXTBase(object): + """DAT CONTEXT""" + default_dump = () + pc_reg = '' + sp_reg = '' + func_result_reg = '' + special_reg_type = {} + + + def regs(self, to_dump=None): + """Return the name and values of the registers + + :returns: [(reg_name, value)] -- A :class:`list` of :class:`tuple`""" + res = [] + if to_dump is None: + to_dump = self.default_dump + for name in to_dump: + value = getattr(self, name) + if name in self.special_reg_type: + value = self.special_reg_type[name](value) + res.append((name, value)) + return res + + def dump(self, to_dump=None): + """Dump (print) the current context""" + regs = self.regs() + for name, value in regs: + print("{0} -> {1}".format(name, hex(value))) + return None + + def get_pc(self): + return getattr(self, self.pc_reg) + + def set_pc(self, value): + return setattr(self, self.pc_reg, value) + + def get_sp(self): + return getattr(self, self.sp_reg) + + def set_sp(self, value): + return setattr(self, self.sp_reg, value) + + def get_func_result(self): + return getattr(self, self.func_result_reg) + + def set_func_result(self, value): + return setattr(self, self.func_result_reg, value) + + pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)") + sp = property(get_sp, set_sp, None, "Stack Pointer register (ESP or RSP)") + func_result = property(get_func_result, set_func_result, None, "Function Resultat register (EAX or RAX)") + + @property + def EEFlags(self): + """Enhanced view of the Eflags (you also have ``EFlags`` for the raw value) + + :type: :class:`EEflags` + """ + off = type(self).EFlags.offset + x = EEflags.from_address(ctypes.addressof(self) + off) + x.self = self + return x + + @property + def EDr7(self): + """Enhanced view of the DR7 register (you also have ``Dr7`` for the raw value) + + :type: :class:`EDr7` + """ + off = type(self).Dr7.offset + x = EDr7.from_address(ctypes.addressof(self) + off) + x.self = self + return x + +
[docs]class ECONTEXT32(ECONTEXTBase, CONTEXT32): + default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') + pc_reg = 'Eip' + sp_reg = 'Esp' + func_result_reg = 'Eax' + fields = [f[0] for f in CONTEXT32._fields_] + """The fields of the structure"""
+ +
[docs]class ECONTEXTWOW64(ECONTEXTBase, WOW64_CONTEXT): + default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') + pc_reg = 'Eip' + sp_reg = 'Esp' + func_result_reg = 'Eax' + fields = [f[0] for f in WOW64_CONTEXT._fields_] + """The fields of the structure"""
+ + +
[docs]class ECONTEXT64(ECONTEXTBase, CONTEXT64): + default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rdx', 'Rbp', 'Rdi', 'Rsi', + 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags') + pc_reg = 'Rip' + sp_reg = 'Rsp' + func_result_reg = 'Rax' + fields = [f[0] for f in CONTEXT64._fields_] + """The fields of the structure""" + + @classmethod +
[docs] def new_aligned(cls): + """Return a new :class:`ECONTEXT64` aligned on 16 bits + + temporary workaround or horrible hack ? choose your side + """ + size = ctypes.sizeof(cls) + nb_qword = (size + 8) / ctypes.sizeof(ULONGLONG) + buffer = (nb_qword * ULONGLONG)() + struct_address = ctypes.addressof(buffer) + if (struct_address & 0xf) not in [0, 8]: + raise ValueError("ULONGLONG array not aligned on 8") + if (struct_address & 0xf) == 8: + struct_address += 8 + self = cls.from_address(struct_address) + # Keep the raw buffer alive + self._buffer = buffer + return self
+ +def bitness(): + """Return 32 or 64""" + import platform + bits = platform.architecture()[0] + return int(bits[:2]) + +if bitness() == 32: + ECONTEXT = ECONTEXT32 +else: + ECONTEXT = ECONTEXT64 + + +
[docs]class EEXCEPTION_POINTERS(ctypes.Structure): + _fields_ = [ + ("ExceptionRecord", ctypes.POINTER(EEXCEPTION_RECORD)), + ("ContextRecord", ctypes.POINTER(ECONTEXT)), + ] + +
[docs] def dump(self): + """Dump (print) the EEXCEPTION_POINTERS""" + record = self.ExceptionRecord[0] + print("Dumping Exception: ") + print(" ExceptionCode = {0} at {1}".format(record.ExceptionCode, hex(record.ExceptionAddress))) + regs = self.ContextRecord[0].regs() + for name, value in regs: + print(" {0} -> {1}".format(name, hex(value)))
+ + +
[docs]class VectoredException(object): + """A decorator that create a callable which can be passed to :func:`AddVectoredExceptionHandler`""" + func_type = ctypes.WINFUNCTYPE(ctypes.c_uint, ctypes.POINTER(EEXCEPTION_POINTERS)) + + def __new__(cls, func): + self = object.__new__(cls) + self.func = func + v = self.func_type(self.decorator) + v.self = self + return v + + def decorator(self, exception_pointers): + try: + return self.func(exception_pointers) + except BaseException as e: + import traceback + print("Ignored Python Exception in Vectored Exception: {0}".format(e)) + traceback.print_exc() + return windef.EXCEPTION_CONTINUE_SEARCH
+ + +class VectoredExceptionHandler(object): + def __init__(self, pos, handler): + self.handler = VectoredException(handler) + self.pos = pos + + def __enter__(self): + self.value = windows.winproxy.AddVectoredExceptionHandler(self.pos, self.handler) + return self + + def __exit__(self, exc_type, exc_value, traceback): + windows.winproxy.RemoveVectoredExceptionHandler(self.value) + return False + +class DumpContextOnException(VectoredExceptionHandler): + def __init__(self, exit=False): + self.exit = exit + super(DumpContextOnException, self).__init__(self.print_context_result) + + def print_context_result(self, exception_pointers): + except_record = exception_pointers[0].ExceptionRecord[0] + exception_pointers[0].dump() + sys.stdout.flush() + if self.exit: + windows.current_process.exit() + return 0 + +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/handle.html b/docs/build/html/_modules/windows/winobject/handle.html new file mode 100644 index 0000000..cecf5b9 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/handle.html @@ -0,0 +1,188 @@ + + + + + + + + windows.winobject.handle — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.handle

+import ctypes
+
+import windows
+from windows import winproxy
+from windows.generated_def import windef
+from windows.winobject.process import WinUnicodeString
+from windows.generated_def.winstructs import *
+
+class EPUBLIC_OBJECT_TYPE_INFORMATION(ctypes.Structure):
+    _fields_ = windows.utils.transform_ctypes_fields(PUBLIC_OBJECT_TYPE_INFORMATION, {"TypeName": windows.winobject.process.WinUnicodeString})
+
+
+
[docs]class Handle(SYSTEM_HANDLE): + """A handle of the system""" + @windows.utils.fixedpropety + def process(self): + """The process possessing the handle + + :type: :class:`WinProcess <windows.winobject.process.WinProcess>`""" + "TODO: something smart ? :D" + return [p for p in windows.system.processes if p.pid == self.dwProcessId][0] + + @windows.utils.fixedpropety + def name(self): + """The name of the handle + + :type: :class:`str`""" + return self._get_object_name() + + @windows.utils.fixedpropety + def type(self): + """The type of the handle + + :type: :class:`str`""" + return self._get_object_type() + + def _get_object_name(self): + lh = self.local_handle + size_needed = DWORD() + yyy = ctypes.c_buffer(0x1000) + size_needed = DWORD() + winproxy.NtQueryObject(lh, ObjectNameInformation, ctypes.byref(yyy), ctypes.sizeof(yyy), ctypes.byref(size_needed)) + return WinUnicodeString.from_buffer_copy(yyy[:size_needed.value]).str + + def _get_object_type(self): + lh = self.local_handle + xxx = EPUBLIC_OBJECT_TYPE_INFORMATION() + size_needed = DWORD() + try: + winproxy.NtQueryObject(lh, ObjectTypeInformation, ctypes.byref(xxx), ctypes.sizeof(xxx), ctypes.byref(size_needed)) + except Exception as e: + size = size_needed.value + buffer = ctypes.c_buffer(size) + winproxy.NtQueryObject(lh, ObjectTypeInformation, buffer, size, ctypes.byref(size_needed)) + xxx = EPUBLIC_OBJECT_TYPE_INFORMATION.from_buffer_copy(buffer) + return xxx.TypeName.str + + @windows.utils.fixedpropety + def local_handle(self): + """A local copy of the handle, acquired with ``DuplicateHandle`` + + :type: :class:`int`""" + if self.dwProcessId == windows.current_process.pid: + return self.wValue + res = HANDLE() + winproxy.DuplicateHandle(self.process.handle, self.wValue, windows.current_process.handle, ctypes.byref(res), dwOptions=DUPLICATE_SAME_ACCESS) + return res.value + + def __repr__(self): + return "<{0} value=<0x{1:x}> in process pid={2}>".format(type(self).__name__, self.wValue, self.dwProcessId) + + def __del__(self): + if self.dwProcessId == windows.current_process.pid: + return + if hasattr(self, "_local_handle"): + return winproxy.CloseHandle(self._local_handle)
+ + +def enumerate_handles(): + size_needed = ULONG() + size = 0x1000 + buffer = ctypes.c_buffer(size) + + try: + winproxy.NtQuerySystemInformation(16, buffer, size, ReturnLength=ctypes.byref(size_needed)) + except WindowsError as e: + pass + + size = size_needed.value + 0x1000 + buffer = ctypes.c_buffer(size) + winproxy.NtQuerySystemInformation(16, buffer, size, ReturnLength=ctypes.byref(size_needed)) + x = SYSTEM_HANDLE_INFORMATION.from_buffer(buffer) + class _GENERATED_SYSTEM_HANDLE_INFORMATION(ctypes.Structure): + _fields_ = [ + ("HandleCount", ULONG), + ("Handles", Handle * x.HandleCount), + ] + return list(_GENERATED_SYSTEM_HANDLE_INFORMATION.from_buffer_copy(buffer[:size_needed.value]).Handles) + +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/network.html b/docs/build/html/_modules/windows/winobject/network.html new file mode 100644 index 0000000..a35c60b --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/network.html @@ -0,0 +1,536 @@ + + + + + + + + windows.winobject.network — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.network

+import windows
+import ctypes
+import socket
+import struct
+
+from windows import winproxy
+import windows.generated_def as gdef
+from windows.com import interfaces as cominterfaces
+from windows.generated_def.winstructs import *
+from windows.generated_def.windef import *
+
+
+
[docs]class TCP4Connection(MIB_TCPROW_OWNER_PID): + """A TCP4 socket (connected or listening)""" + @property + def established(self): + """``True`` if connection is established else it's a listening socket""" + return self.dwState == MIB_TCP_STATE_ESTAB + + @property + def remote_port(self): + """:type: :class:`int`""" + if not self.established: + return None + return socket.ntohs(self.dwRemotePort) + + @property + def local_port(self): + """:type: :class:`int`""" + return socket.ntohs(self.dwLocalPort) + + @property + def local_addr(self): + """Local address IP (x.x.x.x) + + :type: :class:`str`""" + return socket.inet_ntoa(struct.pack("<I", self.dwLocalAddr)) + + @property + def remote_addr(self): + """remote address IP (x.x.x.x) + + :type: :class:`str`""" + if not self.established: + return None + return socket.inet_ntoa(struct.pack("<I", self.dwRemoteAddr)) + + @property + def remote_proto(self): + """Identification of the protocol associated with the remote port. + Equals ``remote_port`` if no protocol is associated with it. + + :type: :class:`str` or :class:`int` + """ + try: + return socket.getservbyport(self.remote_port, 'tcp') + except socket.error: + return self.remote_port + + @property + def remote_host(self): + """Identification of the remote hostname. + Equals ``remote_addr`` if the resolution fails + + :type: :class:`str` or :class:`int` + """ + + try: + return socket.gethostbyaddr(self.remote_addr) + except socket.error: + return self.remote_addr + +
[docs] def close(self): + """Close the connection <require elevated process>""" + closing = MIB_TCPROW() + closing.dwState = MIB_TCP_STATE_DELETE_TCB + closing.dwLocalAddr = self.dwLocalAddr + closing.dwLocalPort = self.dwLocalPort + closing.dwRemoteAddr = self.dwRemoteAddr + closing.dwRemotePort = self.dwRemotePort + return winproxy.SetTcpEntry(ctypes.byref(closing))
+ + def __repr__(self): + if not self.established: + return "<TCP IPV4 Listening socket on {0}:{1}>".format(self.local_addr, self.local_port) + return "<TCP IPV4 Connection {s.local_addr}:{s.local_port} -> {s.remote_addr}:{s.remote_port}>".format(s=self)
+ + +
[docs]class TCP6Connection(MIB_TCP6ROW_OWNER_PID): + """A TCP6 socket (connected or listening)""" + @staticmethod + def _str_ipv6_addr(addr): + return ":".join(c.encode('hex') for c in addr) + + @property + def established(self): + """``True`` if connection is established else it's a listening socket""" + return self.dwState == MIB_TCP_STATE_ESTAB + + @property + def remote_port(self): + """:type: :class:`int`""" + if not self.established: + return None + return socket.ntohs(self.dwRemotePort) + + @property + def local_port(self): + """:type: :class:`int`""" + return socket.ntohs(self.dwLocalPort) + + @property + def local_addr(self): + """Local address IP + + :type: :class:`str`""" + return self._str_ipv6_addr(self.ucLocalAddr) + + @property + def remote_addr(self): + """remote address IP + + :type: :class:`str`""" + if not self.established: + return None + return self._str_ipv6_addr(self.ucRemoteAddr) + + @property + def remote_proto(self): + """Equals to ``self.remote_port`` for Ipv6""" + return self.remote_port + + @property + def remote_host(self): + """Equals to ``self.remote_addr`` for Ipv6""" + return self.remote_addr + + def close(self): + raise NotImplementedError("Closing IPV6 connection non implemented") + + def __repr__(self): + if not self.established: + return "<TCP IPV6 Listening socket on {0}:{1}>".format(self.local_addr, self.local_port) + return "<TCP IPV6 Connection {0}:{1} -> {2}:{3}>".format(self.local_addr, self.local_port, self.remote_addr, self.remote_port)
+ + +def get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer): + x = windows.generated_def.winstructs.MIB_TCPTABLE_OWNER_PID.from_buffer(buffer) + nb_entry = x.dwNumEntries + + class _GENERATED_MIB_TCPTABLE_OWNER_PID(ctypes.Structure): + _fields_ = [ + ("dwNumEntries", DWORD), + ("table", TCP4Connection * nb_entry), + ] + + return _GENERATED_MIB_TCPTABLE_OWNER_PID.from_buffer(buffer) + + +def get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer): + x = windows.generated_def.winstructs.MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer) + nb_entry = x.dwNumEntries + + # Struct _MIB_TCP6TABLE_OWNER_PID definitions + class _GENERATED_MIB_TCP6TABLE_OWNER_PID(Structure): + _fields_ = [ + ("dwNumEntries", DWORD), + ("table", TCP6Connection * nb_entry), + ] + + return _GENERATED_MIB_TCP6TABLE_OWNER_PID.from_buffer(buffer) + +
[docs]class Firewall(cominterfaces.INetFwPolicy2): + """The windows firewall""" + @property + def rules(self): + """The rules of the firewall + + :type: [:class:`FirewallRule`] -- A list of rule + """ + ifw_rules = cominterfaces.INetFwRules() + self.get_Rules(ifw_rules) + + nb_rules = gdef.LONG() + ifw_rules.get_Count(nb_rules) + + unknw = cominterfaces.IUnknown() + ifw_rules.get__NewEnum(unknw) + + pVariant = cominterfaces.IEnumVARIANT() + unknw.QueryInterface(pVariant.IID, pVariant) + + count = gdef.ULONG() + var = windows.com.ImprovedVariant() + + rules = [] + for i in range(nb_rules.value): + pVariant.Next(1, var, count) + if not count.value: + break + rule = FirewallRule() + idisp = var.asdispatch + idisp.QueryInterface(rule.IID, rule) + rules.append(rule) + return rules + + @property + def current_profile_types(self): + """Mask of the profiles currently enabled + + :type: :class:`long` + """ + cpt = gdef.LONG() + self.get_CurrentProfileTypes(cpt) + return cpt.value + + @property + def enabled(self): + """A maping of the active firewall profiles + + { + + ``NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_DOMAIN(0x1L)``: ``True`` or ``False``, + + ``NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_PRIVATE(0x2L)``: ``True`` or ``False``, + + ``NET_FW_PROFILE_TYPE2_.NET_FW_PROFILE2_PUBLIC(0x4L)``: ``True`` or ``False``, + + } + + + :type: :class:`dict` + """ + profiles = [gdef.NET_FW_PROFILE2_DOMAIN, gdef.NET_FW_PROFILE2_PRIVATE, gdef.NET_FW_PROFILE2_PUBLIC] + return {prof: self.enabled_for_profile_type(prof) for prof in profiles} + + + def enabled_for_profile_type(self, profile_type): + enabled = gdef.VARIANT_BOOL() + self.get_FirewallEnabled(profile_type, enabled) + return enabled.value
+ + + +
[docs]class FirewallRule(cominterfaces.INetFwRule): + """A rule of the firewall""" + @property + def name(self): + """Name of the rule + + :type: :class:`unicode` + """ + name = gdef.BSTR() + self.get_Name(name) + return name.value + + @property + def description(self): + """Description of the rule + + :type: :class:`unicode` + """ + description = gdef.BSTR() + self.get_Description(description) + return description.value + + @property + def application_name(self): + """Name of the application to which apply the rule + + :type: :class:`unicode` + """ + applicationname = gdef.BSTR() + self.get_ApplicationName(applicationname) + return applicationname.value + + @property + def service_name(self): + """Name of the service to which apply the rule + + :type: :class:`unicode` + """ + servicename = gdef.BSTR() + self.get_ServiceName(servicename) + return servicename.value + + @property + def protocol(self): + """Protocol to which apply the rule + + :type: :class:`long` + """ + protocol = gdef.LONG() + self.get_Protocol(protocol) + return protocol.value + + @property + def local_address(self): + """Local address of the rule + + :type: :class:`unicode` + """ + local_address = gdef.BSTR() + self.get_LocalAddresses(local_address) + return local_address.value + + @property + def remote_address(self): + """Remote address of the rule + + :type: :class:`unicode` + """ + remote_address = gdef.BSTR() + self.get_RemoteAddresses(remote_address) + return remote_address.value + + @property + def direction(self): + """Direction of the rule, values might be: + + * ``NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_IN(0x1L)`` + * ``NET_FW_RULE_DIRECTION_.NET_FW_RULE_DIR_OUT(0x2L)`` + + subclass of :class:`long` + """ + direction = gdef.NET_FW_RULE_DIRECTION() + self.get_Direction(direction) + return direction.value + + @property + def interface_types(self): + """Types of interface of the rule + + :type: :class:`unicode` + """ + interface_type = gdef.BSTR() + self.get_InterfaceTypes(interface_type) + return interface_type.value + + @property + def local_port(self): + """Local port of the rule + + :type: :class:`unicode` + """ + local_port = gdef.BSTR() + self.get_LocalPorts(local_port) + return local_port.value + + @property + def remote_port(self): + """Remote port of the rule + + :type: :class:`unicode` + """ + remote_port = gdef.BSTR() + self.get_RemotePorts(remote_port) + return remote_port.value + + @property + def action(self): + """Action of the rule, values might be: + + * ``NET_FW_ACTION_.NET_FW_ACTION_BLOCK(0x0L)`` + * ``NET_FW_ACTION_.NET_FW_ACTION_ALLOW(0x1L)`` + + subclass of :class:`long` + """ + action = gdef.NET_FW_ACTION() + self.get_Action(action) + return action.value + + @property + def enabled(self): + """``True`` if rule is enabled""" + enabled = gdef.VARIANT_BOOL() + self.get_Enabled(enabled) + return enabled.value + + @property + def grouping(self): + """Grouping of the rule + + :type: :class:`unicode` + """ + grouping = gdef.BSTR() + self.get_RemotePorts(grouping) + return grouping.value + + @property + def icmp_type_and_code(self): + icmp_type_and_code = gdef.BSTR() + self.get_RemotePorts(icmp_type_and_code) + return icmp_type_and_code.value + + def __repr__(self): + return '<{0} "{1}">'.format(type(self).__name__, self.name)
+ +
[docs]class Network(object): + NetFwPolicy2 = windows.com.IID.from_string("E2B3C97F-6AE1-41AC-817A-F6F92166D7DD") + + @property + def firewall(self): + """The firewall of the system + + :type: :class:`Firewall` + """ + windows.com.init() + firewall = Firewall() + windows.com.create_instance(self.NetFwPolicy2, firewall) + return firewall + + @staticmethod + def _get_tcp_ipv4_sockets(): + size = ctypes.c_uint(0) + try: + winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET) + except winproxy.IphlpapiError: + pass # Allow us to set size to the needed value + buffer = (ctypes.c_char * size.value)() + winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET) + t = get_MIB_TCPTABLE_OWNER_PID_from_buffer(buffer) + return list(t.table) + + @staticmethod + def _get_tcp_ipv6_sockets(): + size = ctypes.c_uint(0) + try: + winproxy.GetExtendedTcpTable(None, ctypes.byref(size), ulAf=AF_INET6) + except winproxy.IphlpapiError: + pass # Allow us to set size to the needed value + buffer = (ctypes.c_char * size.value)() + winproxy.GetExtendedTcpTable(buffer, ctypes.byref(size), ulAf=AF_INET6) + t = get_MIB_TCP6TABLE_OWNER_PID_from_buffer(buffer) + return list(t.table) + + + ipv4 = property(lambda self: self._get_tcp_ipv4_sockets()) + """List of TCP IPv4 socket (connection and listening) + + :type: [:class:`TCP4Connection`]""" + + ipv6 = property(lambda self: self._get_tcp_ipv6_sockets()) + """List of TCP IPv6 socket (connection and listening) + + :type: [:class:`TCP6Connection`] + """
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/process.html b/docs/build/html/_modules/windows/winobject/process.html new file mode 100644 index 0000000..b321079 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/process.html @@ -0,0 +1,1445 @@ + + + + + + + + windows.winobject.process — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.process

+import ctypes
+import os
+import copy
+import time
+import struct
+import itertools
+
+from contextlib import contextmanager
+from collections import namedtuple
+
+import windows
+import windows.native_exec.simple_x86 as x86
+import windows.native_exec.simple_x64 as x64
+
+from windows import injection
+from windows import native_exec
+from windows import pe_parse
+from windows import winproxy
+from windows import utils
+from windows.dbgprint import dbgprint
+from windows.generated_def.winstructs import *
+from windows.generated_def.ntstatus import NtStatusException
+from windows.generated_def import windef
+
+from windows.winobject import exception
+
+
+TimeInfo = namedtuple("TimeInfo", ["creation", "exit", "kernel", "user"])
+"""Time information about a process"""
+
+class AutoHandle(object):
+    """An abstract class that allow easy handle creation/destruction/wait"""
+     # Big bypass to prevent missing reference at programm close..
+    _close_function = ctypes.WinDLL("kernel32").CloseHandle
+    def _get_handle(self):
+        raise NotImplementedError("{0} is abstract".format(type(self).__name__))
+
+    @property
+    def handle(self):
+        """An handle on the object
+
+        :type: HANDLE
+
+           .. note::
+                The handle is automaticaly closed when the object is destroyed
+        """
+        if hasattr(self, "_handle"):
+            return self._handle
+        self._handle = self._get_handle()
+        dbgprint("Open handle {0} for {1}".format(hex(self._handle), self), "HANDLE")
+        return self._handle
+
+    def wait(self, timeout=INFINITE):
+        """Wait for the object"""
+        return winproxy.WaitForSingleObject(self.handle, timeout)
+
+    def __del__(self):
+        if hasattr(self, "_handle") and self._handle:
+            dbgprint("Closing Handle {0:#x} for {1}".format(self._handle, self), "HANDLE")
+            self._close_function(self._handle)
+
+
+
[docs]class WinThread(THREADENTRY32, AutoHandle): + """Represent a thread """ + @utils.fixedpropety + def tid(self): + """Thread ID + + :type: :class:`int`""" + return self.th32ThreadID + + @utils.fixedpropety + def owner(self): + """The Process owning the thread + + :type: :class:`WinProcess` + """ + if hasattr(self, "_owner"): + return self._owner + try: + self._owner = [process for process in windows.system.processes if process.pid == self.th32OwnerProcessID][0] + except IndexError: + return None + return self._owner + + @property + def context(self): + """The context of the thread, type depend of the target process. + + :type: :class:`windows.exception.ECONTEXT32` or :class:`windows.exception.ECONTEXT64` or :class:`windows.exception.ECONTEXTWOW64` + """ + if self.owner.bitness == 32 and windows.current_process.bitness == 64: + # Wow64 + x = exception.ECONTEXTWOW64() + x.ContextFlags = CONTEXT_ALL + winproxy.Wow64GetThreadContext(self.handle, x) + return x + + if self.owner.bitness == 64 and windows.current_process.bitness == 32: + x = exception.ECONTEXT64.new_aligned() + x.ContextFlags = CONTEXT_ALL + windows.syswow64.NtGetContextThread_32_to_64(self.handle, x) + return x + + if self.owner.bitness == 32: + x = exception.ECONTEXT32() + else: + x = exception.ECONTEXT64.new_aligned() + x.ContextFlags = CONTEXT_ALL + winproxy.GetThreadContext(self.handle, x) + return x + + @property + def context_syswow(self): + """The 64 bits context of a syswow thread. + + :type: :class:`windows.exception.ECONTEXT64` + """ + if not self.owner.is_wow_64: + raise ValueError("Not a syswow process") + x = exception.ECONTEXT64.new_aligned() + x.ContextFlags = CONTEXT_ALL + if windows.current_process.bitness == 64: + winproxy.GetThreadContext(self.handle, x) + else: + windows.syswow64.NtGetContextThread_32_to_64(self.handle, x) + return x + + +
[docs] def set_context(self, context): + """Set the thread's context to ``context``""" + if self.owner.bitness == windows.current_process.bitness: + return winproxy.SetThreadContext(self.handle, context) + if windows.current_process.bitness == 64 and self.owner.bitness == 32: + return winproxy.Wow64SetThreadContext(self.handle, context) + return windows.syswow64.NtSetContextThread_32_to_64(self.handle, ctypes.byref(context))
+ + +
[docs] def set_syswow_context(self, context): + """Set a syswow thread's 64 context to ``context``""" + if not self.owner.is_wow_64: + raise ValueError("Not a syswow process") + if windows.current_process.bitness == 64: + return winproxy.SetThreadContext(self.handle, context) + return windows.syswow64.NtSetContextThread_32_to_64(self.handle, ctypes.byref(context))
+ + + @property + def start_address(self): + """The start address of the thread + + :type: :class:`int` + """ + if windows.current_process.bitness == 32 and self.owner.bitness == 64: + res = ULONGLONG() + windows.syswow64.NtQueryInformationThread_32_to_64(self.handle, ThreadQuerySetWin32StartAddress, byref(res), ctypes.sizeof(res)) + return res.value + res_size = max(self.owner.bitness, windows.current_process.bitness) + if res_size == 32: + res = ULONG() + else: + res = ULONGLONG() + winproxy.NtQueryInformationThread(self.handle, ThreadQuerySetWin32StartAddress, byref(res), ctypes.sizeof(res)) + return res.value + + @property + def teb_base(self): + """The address of the thread's TEB + + :type: :class:`int` + """ + if windows.current_process.bitness == 32 and self.owner.bitness == 64: + restype = rctypes.transform_type_to_remote64bits(THREAD_BASIC_INFORMATION) + ressize = (ctypes.sizeof(restype)) + # Manual aligned allocation :DDDD + nb_qword = (ressize + 8) / ctypes.sizeof(ULONGLONG) + buffer = (nb_qword * ULONGLONG)() + struct_address = ctypes.addressof(buffer) + if (struct_address & 0xf) not in [0, 8]: + raise ValueError("ULONGLONG array not aligned on 8") + windows.syswow64.NtQueryInformationThread_32_to_64(self.handle, ThreadBasicInformation, struct_address, ressize) + return restype(struct_address, windows.current_process).TebBaseAddress + + res = THREAD_BASIC_INFORMATION() + windows.winproxy.NtQueryInformationThread(self.handle, ThreadBasicInformation, byref(res), ctypes.sizeof(res)) + return res.TebBaseAddress + +
[docs] def exit(self, code=0): + """Exit the thread""" + return winproxy.TerminateThread(self.handle, code)
+ +
[docs] def resume(self): + """Resume the thread""" + return winproxy.ResumeThread(self.handle)
+ +
[docs] def suspend(self): + """Suspend the thread""" + return winproxy.SuspendThread(self.handle)
+ + def _get_handle(self): + return winproxy.OpenThread(dwThreadId=self.tid) + + @property + def is_exit(self): + """``True`` if the thread is terminated + + :type: :class:`bool` + """ + return self.exit_code != STILL_ACTIVE + + @property + def exit_code(self): + """The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead + + :type: :class:`int` + """ + res = DWORD() + winproxy.GetExitCodeThread(self.handle, byref(res)) + return res.value + + def __repr__(self): + owner = self.owner + if owner is None: + owner_name = "<Dead process with pid {0}>".format(hex(self.th32OwnerProcessID)) + else: + owner_name = owner.name + return '<{0} {1} owner "{2}" at {3}>'.format(self.__class__.__name__, self.tid, owner_name, hex(id(self))) + + @staticmethod + def _from_handle(handle): + tid = WinThread._get_thread_id(handle) + try: + # Really useful ? + thread = [t for t in windows.winobject.system.System().threads if t.tid == tid][0] + # set AutoHandle _handle + thread._handle = handle + dbgprint("Thread {0} from handle {1}".format(thread, hex(handle)), "HANDLE") + return thread + except IndexError: + dbgprint("DeadThread from handle {0}".format(hex(handle)), "HANDLE") + return DeadThread(handle, tid) + + @staticmethod + def _get_thread_id_by_api(handle): + return winproxy.GetThreadId(handle) + + @staticmethod + def _get_thread_id_manual(handle): + if windows.current_process.bitness == 32 and self.owner.bitness == 64: + raise NotImplementedError("[_get_thread_id_manual] 32 -> 64 (XP64 bits + Syswow process ?)") + res = THREAD_BASIC_INFORMATION() + windows.winproxy.NtQueryInformationThread(hand, ThreadBasicInformation, byref(res), ctypes.sizeof(res)) + id2 = res.ClientId.UniqueThread + return id2 + + if winproxy.is_implemented(winproxy.GetThreadId): + _get_thread_id = _get_thread_id_by_api + else: + _get_thread_id = _get_thread_id_manual
+ + +
[docs]class DeadThread(AutoHandle): + """An already dead thread (returned only by API returning a new thread if thread die before being returned)""" + def __init__(self, handle, tid=None): + if tid is None: + tid = WinThread._get_thread_id(handle) + self.tid = tid + # set AutoHandle _handle + self._handle = handle + + @property + def is_exit(self): + """``True`` if the thread is terminated + + :type: :class:`bool` + """ + return self.exit_code != STILL_ACTIVE + + @property + def exit_code(self): + """The exit code of the thread : ``STILL_ACTIVE`` means the process is not dead + + :type: :class:`int` + """ + res = DWORD() + winproxy.GetExitCodeThread(self.handle, byref(res)) + return res.value
+ + +class Process(AutoHandle): + @utils.fixedpropety + def is_wow_64(self): + """``True`` if the process is a SysWow64 process (32bit process on 64bits system). + + :type: :class:`bool` + """ + return utils.is_wow_64(self.handle) + + @utils.fixedpropety + def bitness(self): + """The bitness of the process + + :returns: :class:`int` -- 32 or 64 + """ + if windows.system.bitness == 32: + return 32 + if self.is_wow_64: + return 32 + return 64 + + @property + def threads(self): + """The threads of the process + + :type: [:class:`WinThread`] -- A list of Thread + """ + return [thread for thread in windows.system.threads if thread.th32OwnerProcessID == self.pid] + + def virtual_alloc(self, size): + raise NotImplementedError("virtual_alloc") + + def virtual_free(self): + raise NotImplementedError("virtual_free") + + @property + def exit_code(self): + """The exit code of the process : ``STILL_ACTIVE`` means the process is not dead + + :type: :class:`int` + """ + res = DWORD() + winproxy.GetExitCodeProcess(self.handle, byref(res)) + return res.value + + @property + def is_exit(self): + """``True`` if the process is terminated + + :type: :class:`bool` + """ + return self.exit_code != STILL_ACTIVE + + @contextmanager + def allocated_memory(self, size): + """ContextManager to allocate memory and free it + + :type: :class:`int` -- the address of the allocated memory + """ + addr = self.virtual_alloc(size) + try: + yield addr + finally: + winproxy.VirtualFreeEx(self.handle, addr) + + @contextmanager + def virtual_protected(self, addr, size, protect): + """A context manager for local virtual_protect (old Protection are restored at exit)""" + old_protect = DWORD() + self.virtual_protect(addr, size, protect, old_protect) + try: + yield addr + finally: + self.virtual_protect(addr, size, old_protect.value, old_protect) + + def virtual_protect(self, addr, size, protect, old_protect): + """Change the access right of one or more page of the process""" + if windows.current_process.bitness == 32 and self.bitness == 64: + #addr = (addr >> 12) << 12 + #addr = ULONG64(addr) + if size & 0x0fff: + size = ((size >> 12) + 1) << 12 + #ssize = ULONG(size) + #import pdb;pdb.set_trace() + old_protect = ctypes.addressof(old_protect) + xaddr = ULONG64(addr) + addr = ctypes.addressof(xaddr) + xsize = ULONG(size) + size = ctypes.addressof(xsize) + return windows.syswow64.NtProtectVirtualMemory_32_to_64(self.handle, addr, size, protect, old_protect) + else: + winproxy.VirtualProtectEx(self.handle, addr, size, protect, old_protect) + + + def execute(self, code, parameter=0): + """Execute some native code in the context of the process + + :return: The thread executing the code + :rtype: :class:`WinThread` or :class:`DeadThread` + """ + x = self.virtual_alloc(len(code)) #Todo: free this ? when ? how ? reuse ? + self.write_memory(x, code) + return self.create_thread(x, parameter) + + def query_memory(self, addr): + """Query the memory informations about page at ``addr`` + + :rtype: :class:`MEMORY_BASIC_INFORMATION` + """ + if windows.current_process.bitness == 32 and self.bitness == 64: + res = MEMORY_BASIC_INFORMATION64() + try: + v = windows.syswow64.NtQueryVirtualMemory_32_to_64(ProcessHandle=self.handle, BaseAddress=addr, MemoryInformationClass=MemoryBasicInformation, MemoryInformation=res) + except NtStatusException as e: + if e.code & 0xffffffff == 0XC000000D: + raise winproxy.Kernel32Error("NtQueryVirtualMemory_32_to_64") + raise + return res + + info_type = {32 : MEMORY_BASIC_INFORMATION32, 64 : MEMORY_BASIC_INFORMATION64} + res = info_type[windows.current_process.bitness]() + ptr = ctypes.cast(byref(res), POINTER(MEMORY_BASIC_INFORMATION)) + winproxy.VirtualQueryEx(self.handle, addr, ptr, sizeof(res)) + return res + + def memory_state(self): + """Yield the memory information for the whole address space of the process + + :yield: :class:`MEMORY_BASIC_INFORMATION` + """ + addr = 0 + res = [] + while True: + try: + x = self.query_memory(addr) + yield x + except winproxy.Kernel32Error: + return + addr += x.RegionSize + + def query_working_set(self): + if self.bitness == 64 or windows.current_process.bitness == 64: + WSET_BLOCK = EPSAPI_WORKING_SET_BLOCK64 + dummy = PSAPI_WORKING_SET_INFORMATION64() + else: + WSET_BLOCK = EPSAPI_WORKING_SET_BLOCK32 + dummy = PSAPI_WORKING_SET_INFORMATION32() + try: + windows.winproxy.QueryWorkingSet(self.handle, ctypes.byref(dummy), ctypes.sizeof(dummy)) + except WindowsError as e: + if e.winerror != 24: + raise + + NumberOfEntriesType = [f for f in WSET_BLOCK._fields_ if f[0] == "Flags"][0][1] + for i in range(10): + # use the same type as WSET_BLOCK.Flags + class GENERATED_PSAPI_WORKING_SET_INFORMATION(ctypes.Structure): + _fields_ = [ + ("NumberOfEntries", NumberOfEntriesType), + ("WorkingSetInfo", WSET_BLOCK * dummy.NumberOfEntries), + ] + res = GENERATED_PSAPI_WORKING_SET_INFORMATION() + try: + if windows.current_process.bitness == 32 and self.bitness == 64: + windows.syswow64.NtQueryVirtualMemory_32_to_64(self.handle, 0, MemoryWorkingSetList, res) + else: + windows.winproxy.QueryWorkingSet(self.handle, ctypes.byref(res), ctypes.sizeof(res)) + except WindowsError as e: + if e.winerror != 24: + raise + dummy.NumberOfEntries = res.NumberOfEntries + continue + except windows.generated_def.ntstatus.NtStatusException as e: + if e.code != STATUS_INFO_LENGTH_MISMATCH: + raise + dummy.NumberOfEntries = res.NumberOfEntries + continue + return res.WorkingSetInfo + # Raise ? + return None + + def query_working_setex(self, addresses): + if self.bitness == 64 or windows.current_process.bitness == 64: + info_type = EPSAPI_WORKING_SET_EX_INFORMATION64 + else: + info_type = EPSAPI_WORKING_SET_EX_INFORMATION32 + info_array = (info_type * len(addresses))() + for i, data in enumerate(info_array): + info_array[i].VirtualAddress = addresses[i] + if windows.current_process.bitness == 32 and self.bitness == 64: + windows.syswow64.NtQueryVirtualMemory_32_to_64(self.handle, 0, MemoryWorkingSetListEx, info_array) + else: + winproxy.QueryWorkingSetEx(self.handle, ctypes.byref(info_array), ctypes.sizeof(info_array)) + return info_array + + + def get_mapped_filename(self, addr): + """The filename mapped at address ``addr`` or ``None`` + + :rtype: :class:`str` or ``None`` + """ + buffer_size = 0x1000 + buffer = ctypes.c_buffer(buffer_size) + + if windows.current_process.bitness == 32 and self.bitness == 64: + target_size = ctypes.c_buffer(buffer_size) + try: + windows.syswow64.NtQueryVirtualMemory_32_to_64(self.handle, addr, MemorySectionName, buffer, buffer_size, target_size) + except NtStatusException as e: + if e.code not in [STATUS_FILE_INVALID, STATUS_INVALID_ADDRESS, STATUS_TRANSACTION_NOT_ACTIVE]: + raise + return None + remote_winstring = rctypes.transform_type_to_remote64bits(WinUnicodeString) + mapped_filename = remote_winstring(ctypes.addressof(buffer), windows.current_process) + return mapped_filename.str + + try: + size = winproxy.GetMappedFileNameA(self.handle, addr, buffer, buffer_size) + except winproxy.Kernel32Error as e: + return None + return buffer[:size] + + def read_byte(self, addr): + """Read a ``CHAR`` at ``addr``""" + sizeof_char = sizeof(CHAR) + return struct.unpack("<B", self.read_memory(addr, sizeof_char))[0] + + def read_short(self, addr): + """Read a ``SHORT`` at ``addr``""" + sizeof_short = sizeof(ctypes.c_short) + return struct.unpack("<H", self.read_memory(addr, sizeof_short))[0] + + def read_dword(self, addr): + """Read a ``DWORD`` at ``addr``""" + sizeof_dword = sizeof(DWORD) + return struct.unpack("<I", self.read_memory(addr, sizeof_dword))[0] + + def read_qword(self, addr): + """Read a ``ULONG64`` at ``addr``""" + sizeof_qword = sizeof(ULONG64) + return struct.unpack("<Q", self.read_memory(addr, sizeof_qword))[0] + + def read_ptr(self, addr): + """Read a ``PTR`` at ``addr``""" + if self.bitness == 32: + return self.read_dword(addr) + return self.read_qword(addr) + + def read_string(self, addr): + """Read an ascii string at ``addr``""" + res = [] + for i in itertools.count(): + x = self.read_memory(addr + (i * 0x100), 0x100) + if "\x00" in x: + res.append(x.split("\x00", 1)[0]) + break + res.append(x) + return "".join(res) + + def read_wstring(self, addr): + """Read a windows UTF16 string at ``addr``""" + res = [] + for i in itertools.count(): + x = self.read_memory(addr + (i * 0x100), 0x100) + utf16_chars = ["".join(c) for c in zip(*[iter(x)] * 2)] + if "\x00\x00" in utf16_chars: + res.extend(utf16_chars[:utf16_chars.index("\x00\x00")]) + break + res.extend(x) + return "".join(res).decode('utf16') + + def write_byte(self, addr, byte): + """write a byte at ``addr``""" + return self.write_memory(addr, struct.pack("<B", byte)) + + def write_short(self, addr, word): + """write a word at ``addr``""" + return self.write_memory(addr, struct.pack("<H", word)) + + def write_dword(self, addr, dword): + """write a dword at ``addr``""" + return self.write_memory(addr, struct.pack("<I", dword)) + + def write_qword(self, addr, qword): + """write a qword at ``addr``""" + return self.write_memory(addr, struct.pack("<Q", qword)) + + @property + def time_info(self): + """The time information of the process (creation, kernel/user time, exit time) + + :type: :class:`TimeInfo`""" + CreationTime = FILETIME() + ExitTime = FILETIME() + KernelTime = FILETIME() + UserTime = FILETIME() + winproxy.GetProcessTimes(self.handle, CreationTime, ExitTime, KernelTime, UserTime) + + creation = (CreationTime.dwHighDateTime << 32) + CreationTime.dwLowDateTime + exit = (ExitTime.dwHighDateTime << 32) + ExitTime.dwLowDateTime + kernel = (KernelTime.dwHighDateTime << 32) + KernelTime.dwLowDateTime + user = (UserTime.dwHighDateTime << 32) + UserTime.dwLowDateTime + + return TimeInfo(creation, exit, kernel, user) + + @utils.fixedpropety + def token(self): + """The token of the process + + :type: :class:`Token` + """ + token_handle = HANDLE() + winproxy.OpenProcessToken(self.handle, TOKEN_QUERY, byref(token_handle)) + return Token(token_handle.value) + +
[docs]class CurrentThread(AutoHandle): + """The current thread""" + @property #It's not a fixedpropety because executing thread might change + def tid(self): + """Thread ID + + :type: :class:`int` + """ + return winproxy.GetCurrentThreadId() + + @utils.fixedpropety + def owner(self): + """The current process + + :type: :class:`CurrentProcess` + """ + return windows.current_process + + def _get_handle(self): + return winproxy.GetCurrentThread() + + def __del__(self): + pass + +
[docs] def exit(self, code=0): + """Exit the thread""" + return winproxy.ExitThread(code)
+ +
[docs] def wait(self, timeout=INFINITE): + """Raise :class:`ValueError` to prevent deadlock :D""" + raise ValueError("wait() on current thread")
+ + +
[docs]class CurrentProcess(Process): + """The current process""" + get_peb = None + + get_peb_32_code = x86.MultipleInstr() + get_peb_32_code += x86.Mov('EAX', x86.mem('fs:[0x30]')) + get_peb_32_code += x86.Ret() + get_peb_32_code = get_peb_32_code.get_code() + + get_peb_64_code = x64.MultipleInstr() + get_peb_64_code += x64.Mov('RAX', x64.mem('gs:[0x60]')) + get_peb_64_code += x64.Ret() + get_peb_64_code = get_peb_64_code.get_code() + + allocator = native_exec.native_function.allocator + + # Use RtlGetCurrentPeb ? + def get_peb_builtin(self): + if self.get_peb is not None: + return self.get_peb + if self.bitness == 32: + get_peb = native_exec.create_function(self.get_peb_32_code, [PVOID]) + else: + get_peb = native_exec.create_function(self.get_peb_64_code, [PVOID]) + self.get_peb = get_peb + return get_peb + + def _get_handle(self): + return winproxy.GetCurrentProcess() + + def __del__(self): + pass + + @property + def pid(self): + """Process ID + + :type: :class:`int` + """ + return os.getpid() + + # Is there a better way ? + @utils.fixedpropety + def ppid(self): + """Parent Process ID + + :type: :class:`int` + """ + return [p for p in windows.system.processes if p.pid == self.pid][0].ppid + + @utils.fixedpropety + def peb(self): + """The Process Environment Block of the current process + + :type: :class:`PEB` + """ + return PEB.from_address(self.get_peb_builtin()()) + + @utils.fixedpropety + def bitness(self): + """The bitness of the process + + :type: :class:`int` -- 32 or 64 + """ + import platform + bits = platform.architecture()[0] + return int(bits[:2]) + +
[docs] def virtual_alloc(self, size, prot=PAGE_EXECUTE_READWRITE): + """Allocate memory in the process + + :return: The address of the allocated memory + :rtype: :class:`int` + """ + return winproxy.VirtualAlloc(dwSize=size, flProtect=prot)
+ +
[docs] def virtual_free(self, addr): + """Free memory in the process by virtual_alloc""" + return winproxy.VirtualFree(addr)
+ +
[docs] def write_memory(self, addr, data): + """Write data at addr""" + buffertype = (c_char * len(data)).from_address(addr) + buffertype[:len(data)] = data + return True
+ +
[docs] def read_memory(self, addr, size): + """Read ``size`` from ``addr`` + + :return: The data read + :rtype: :class:`str` + """ + dbgprint('Read CurrentProcess Memory', 'READMEM') + buffer = (c_char * size).from_address(addr) + return buffer[:]
+ +
[docs] def create_thread(self, lpStartAddress, lpParameter, dwCreationFlags=0): + """Create a new thread + + :rtype: :class:`WinThread` or :class:`DeadThread` + """ + handle = winproxy.CreateThread(lpStartAddress=lpStartAddress, lpParameter=lpParameter, dwCreationFlags=dwCreationFlags) + return WinThread._from_handle(handle)
+ +
[docs] def execute(self, code, parameter=0): + """Execute native code ``code`` in the current thread. + + :rtype: :class:`int` the return value of the native code""" + f = windows.native_exec.create_function(code, [PVOID, PVOID]) + return f(parameter)
+ +
[docs] def exit(self, code=0): + """Exit the process""" + return winproxy.ExitProcess(code)
+ +
[docs] def wait(self, timeout=INFINITE): + """Raise :class:`ValueError` to prevent deadlock :D""" + raise ValueError("wait() on current thread")
+ + @utils.fixedpropety + def peb_syswow(self): + """The 64bits PEB of a SysWow64 process + + :type: :class:`PEB` + """ + if not self.is_wow_64: + raise ValueError("Not a syswow process") + return windows.syswow64.get_current_process_syswow_peb()
+ +
[docs]class WinProcess(Process): + """A Process on the system""" + def __init__(self, pid=None, handle=None, name=None, ppid=None): + if pid is None and handle is None: + raise ValueError("Need at lead <pid> or <handle> to create a {0}".format(type(self).__name)) + + if pid is not None: self._pid = pid + if handle is not None: self._handle = handle + if name is not None: self._name = name + if ppid is not None: self._ppid = ppid + + + @staticmethod + def _from_handle(handle): + #pid = winproxy.GetProcessId(handle) + #proc = [p for p in windows.system.processes if p.pid == pid][0] + #proc._handle = handle + #dbgprint("Process {0} from handle {1}".format(proc, hex(handle)), "HANDLE") + return WinProcess(handle=handle) + + @classmethod + def _from_PROCESSENTRY32(cls, entry): + #print("_from_PROCESSENTRY32") + name = entry.szExeFile.decode() + pid = entry.th32ProcessID + ppid = entry.th32ParentProcessID + return WinProcess(pid=pid, name=name, ppid=ppid) + + + @utils.fixedpropety + def name(self): + """Name of the process + + :type: :class:`str` + """ + buffer = ctypes.c_buffer(0x1024) + rsize = winproxy.GetProcessImageFileNameA(self.handle, buffer) + # GetProcessImageFileNameA returns the fullpath + return buffer[:rsize].decode().split("\\")[-1] + + @utils.fixedpropety + def pid(self): + """Process ID + + :type: :class:`int` + """ + return winproxy.GetProcessId(self.handle) + + @utils.fixedpropety + def ppid(self): + """Parent Process ID + + :type: :class:`int` + """ + # TODO: is there an API ? + pid = self.pid + return [p for p in windows.system.processes if p.pid == pid][0].th32ParentProcessID + + def _get_handle(self): + return winproxy.OpenProcess(dwProcessId=self.pid) + + def __repr__(self): + try: + if self.is_exit: + return '<{0} "{1}" pid {2} (DEAD) at {3}>'.format(self.__class__.__name__, self.name, self.pid, hex(id(self))) + except WindowsError: # Cannot open process + pass + return '<{0} "{1}" pid {2} at {3}>'.format(self.__class__.__name__, self.name, self.pid, hex(id(self))) + +
[docs] def virtual_alloc(self, size, prot=PAGE_EXECUTE_READWRITE): + """Allocate memory in the process + + :return: The address of the allocated memory + :rtype: :class:`int` + """ + return winproxy.VirtualAllocEx(self.handle, dwSize=size, flProtect=prot)
+ +
[docs] def virtual_free(self, addr): + """Free memory in the process by virtual_alloc""" + return winproxy.VirtualFreeEx(self.handle, addr)
+ +
[docs] def write_memory(self, addr, data): + """Write `data` at `addr`""" + if windows.current_process.bitness == 32 and self.bitness == 64: + if not winproxy.is_implemented(winproxy.NtWow64WriteVirtualMemory64): + raise ValueError("NtWow64WriteVirtualMemory64 non available in ntdll: cannot write into 64bits processus") + return winproxy.NtWow64WriteVirtualMemory64(self.handle, addr, data, len(data)) + return winproxy.WriteProcessMemory(self.handle, addr, lpBuffer=data)
+ + def low_read_memory(self, addr, buffer_addr, size): + if windows.current_process.bitness == 32 and self.bitness == 64: + # OptionalExport can be None (see winproxy.py) + if not winproxy.is_implemented(winproxy.NtWow64ReadVirtualMemory64): + raise ValueError("NtWow64ReadVirtualMemory64 non available in ntdll: cannot read into 64bits processus") + return winproxy.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size) + #if self.is_wow_64 and addr > 0xffffffff: + # return winproxy.NtWow64ReadVirtualMemory64(self.handle, addr, buffer_addr, size) + return winproxy.ReadProcessMemory(self.handle, addr, lpBuffer=buffer_addr, nSize=size) + +
[docs] def read_memory(self, addr, size): + """Read ``size`` from ``addr`` + + :return: The data read + :rtype: :class:`str` + """ + buffer = ctypes.create_string_buffer(size) + self.low_read_memory(addr, ctypes.byref(buffer), size) + return buffer[:]
+ + # Simple cache test + # real_read = read_memory + # + # def read_memory(self, addr, size): + # """Cached version for test""" + # dbgprint('Read remote Memory of {0}'.format(self), 'READMEM') + # if not hasattr(self, "_cache_cache"): + # self._cache_cache = {} + # page_addr = addr & 0xfffffffffffff000 + # if page_addr in self._cache_cache: + # #print("CACHED Read on page {0}".format(hex(page_addr))) + # page_data = self._cache_cache[page_addr] + # return page_data[addr & 0xfff: (addr & 0xfff) + size] + # else: + # page_data = self.real_read(page_addr, 0x1000) + # self._cache_cache[page_addr] = page_data + # return page_data[addr & 0xfff: (addr & 0xfff) + size] + +
[docs] def read_memory_into(self, addr, struct): + """Read a :mod:`ctypes` struct from `addr` + + :returns: struct + """ + self.low_read_memory(addr, ctypes.byref(struct), ctypes.sizeof(struct)) + return struct
+ +
[docs] def create_thread(self, addr, param): + """Create a remote thread + + :rtype: :class:`WinThread` or :class:`DeadThread` + """ + if windows.current_process.bitness == 32 and self.bitness == 64: + thread_handle = HANDLE() + windows.syswow64.NtCreateThreadEx_32_to_64(ThreadHandle=byref(thread_handle) ,ProcessHandle=self.handle, lpStartAddress=addr, lpParameter=param) + return WinThread._from_handle(thread_handle.value) + return WinThread._from_handle(winproxy.CreateRemoteThread(hProcess=self.handle, lpStartAddress=addr, lpParameter=param))
+ +
[docs] def load_library(self, dll_path): + """Load the library in remote process""" + return windows.injection.load_dll_in_remote_process(self, dll_path)
+ +
[docs] def execute_python(self, pycode): + """Execute Python code into the remote process. + + This function waits for the remote process to end and + raises an exception if the remote thread raised one + """ + return injection.safe_execute_python(self, pycode)
+ +
[docs] def execute_python_unsafe(self, pycode): + """Execute Python code into the remote process. + + :rtype: :rtype: :class:`WinThread` or :class:`DeadThread` : The thread executing the python code + """ + return injection.execute_python_code(self, pycode)
+ + @utils.fixedpropety + def peb_addr(self): + """The address of the PEB + + :type: :class:`int` + """ + if windows.current_process.bitness == 32 and self.bitness == 64: + x = windows.remotectypes.transform_type_to_remote64bits(PROCESS_BASIC_INFORMATION) + # Fuck-it <3 + data = (ctypes.c_char * ctypes.sizeof(x))() + windows.syswow64.NtQueryInformationProcess_32_to_64(self.handle, ProcessInformation=data, ProcessInformationLength=ctypes.sizeof(x)) + peb_offset = x.PebBaseAddress.offset + peb_addr = struct.unpack("<Q", data[x.PebBaseAddress.offset: x.PebBaseAddress.offset+8])[0] + elif windows.current_process.bitness == 64 and self.bitness == 32: + information_type = 26 + y = ULONGLONG() + winproxy.NtQueryInformationProcess(self.handle, information_type, byref(y), sizeof(y)) + peb_addr = y.value + else: + information_type = 0 + x = PROCESS_BASIC_INFORMATION() + winproxy.NtQueryInformationProcess(self.handle, information_type, x) + peb_addr = ctypes.cast(x.PebBaseAddress, PVOID).value + if peb_addr is None: + raise ValueError("Could not get peb addr of process {0}".format(self.name)) + return peb_addr + + @utils.fixedpropety + def peb(self): + """The PEB of the process (see :mod:`remotectypes`) + + :type: :class:`PEB` + """ + if windows.current_process.bitness == 32 and self.bitness == 64: + return RemotePEB64(self.peb_addr, self) + if windows.current_process.bitness == 64 and self.bitness == 32: + return RemotePEB32(self.peb_addr, self) + return RemotePEB(self.peb_addr, self) + + @utils.fixedpropety + def peb_syswow(self): + """The 64bits PEB of a SysWow64 process + + :type: :class:`PEB` + """ + if not self.is_wow_64: + raise ValueError("Not a syswow process") + if windows.current_process.bitness == 64: + information_type = 0 + x = PROCESS_BASIC_INFORMATION() + winproxy.NtQueryInformationProcess(self.handle, information_type, x) + peb_addr = ctypes.cast(x.PebBaseAddress, PVOID).value + return RemotePEB(peb_addr, self) + else: #current is 32bits + x = windows.remotectypes.transform_type_to_remote64bits(PROCESS_BASIC_INFORMATION) + # Fuck-it <3 + data = (ctypes.c_char * ctypes.sizeof(x))() + windows.syswow64.NtQueryInformationProcess_32_to_64(self.handle, ProcessInformation=data, ProcessInformationLength=ctypes.sizeof(x)) + peb_offset = x.PebBaseAddress.offset + peb_addr = struct.unpack("<Q", data[x.PebBaseAddress.offset: x.PebBaseAddress.offset+8])[0] + return RemotePEB64(peb_addr, windows.syswow64.ReadSyswow64Process(self)) + +
[docs] def exit(self, code=0): + """Exit the process""" + return winproxy.TerminateProcess(self.handle, code)
+ +KNOW_INTEGRITY_LEVEL = [ +SECURITY_MANDATORY_UNTRUSTED_RID, +SECURITY_MANDATORY_LOW_RID, +SECURITY_MANDATORY_MEDIUM_RID, +SECURITY_MANDATORY_MEDIUM_PLUS_RID, +SECURITY_MANDATORY_HIGH_RID, +SECURITY_MANDATORY_SYSTEM_RID, +SECURITY_MANDATORY_PROTECTED_PROCESS_RID] + +know_integrity_level_mapper = {x:x for x in KNOW_INTEGRITY_LEVEL} + +# Create ProcessToken and Thread Token objects ? +
[docs]class Token(AutoHandle): + """The token of a process""" + def __init__(self, handle): + self._handle = handle + + @property + def integrity(self): + """Return the integrity level of a process + + :type: :class:`int` + """ + buffer_size = self.get_required_information_size(TokenIntegrityLevel) + buffer = ctypes.c_buffer(buffer_size) + self.get_informations(TokenIntegrityLevel, buffer) + + sid = ctypes.cast(buffer, POINTER(TOKEN_MANDATORY_LABEL))[0].Label.Sid + count = winproxy.GetSidSubAuthorityCount(sid) + integrity = winproxy.GetSidSubAuthority(sid, ord(count[0]) - 1)[0] + return know_integrity_level_mapper.get(integrity, integrity) + + @property + def is_elevated(self): + """``True`` if process is Admin""" + elevation = TOKEN_ELEVATION() + self.get_informations(TokenElevation, elevation) + return bool(elevation.TokenIsElevated) + + @property + def token_user(self): + buffer_size = self.get_required_information_size(TokenUser) + buffer = ctypes.c_buffer(buffer_size) + self.get_informations(TokenUser, buffer) + return ctypes.cast(buffer, POINTER(TOKEN_USER))[0] + + @property + def computername(self): + """The computername of the token""" + return self._user_and_computer_name()[1] + + @property + def username(self): + """The username of the token""" + return self._user_and_computer_name()[0] + + def _user_and_computer_name(self): + tok_usr = self.token_user + sid = tok_usr.User.Sid + usernamesize = DWORD(0x1000) + computernamesize = DWORD(0x1000) + username = ctypes.c_buffer(usernamesize.value) + computername = ctypes.c_buffer(computernamesize.value) + peUse = SID_NAME_USE() + winproxy.LookupAccountSidA(None, sid, username, usernamesize, computername, computernamesize, peUse) + return username[:usernamesize.value], computername[:computernamesize.value] + + def get_informations(self, info_type, data): + cbsize = DWORD() + winproxy.GetTokenInformation(self.handle, info_type, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(cbsize)) + return cbsize.value + + def get_required_information_size(self, info_type): + cbsize = DWORD() + try: + winproxy.GetTokenInformation(self.handle, info_type, None, 0, ctypes.byref(cbsize)) + except WindowsError: + pass + return cbsize.value
+ + +def transform_ctypes_fields(struct, replacement): + return [(name, replacement.get(name, type)) for name, type in struct._fields_] + + +
[docs]class WinUnicodeString(Structure): + """LSA_UNICODE_STRING with a nice `__repr__`""" + _fields_ = transform_ctypes_fields(LSA_UNICODE_STRING, {"Buffer": ctypes.c_void_p}) + fields = [f[0] for f in _fields_] + """The fields of the structure""" + + @property + def str(self): + """The python string of the LSA_UNICODE_STRING object + + :type: :class:`unicode` + """ + if not self.Length: + return "" + if getattr(self, "_target", None) is not None: #remote ctypes :D -> TRICKS OF THE YEAR + raw_data = self._target.read_memory(self.Buffer, self.Length) + return raw_data.decode("utf16") + size = self.Length / 2 + return (ctypes.c_wchar * size).from_address(self.Buffer)[:] + + def __repr__(self): + return """<{0} "{1}" at {2}>""".format(type(self).__name__, self.str, hex(id(self)))
+ + +
[docs]class LoadedModule(Structure): + _fields_ = transform_ctypes_fields(LDR_DATA_TABLE_ENTRY, {"BaseDllName": WinUnicodeString, "FullDllName": WinUnicodeString}) + """An entry in the PEB Ldr list""" + @property + def baseaddr(self): + """Base address of the module + + :type: :class:`int` + """ + return self.DllBase + + @property + def name(self): + """Name of the module + + :type: :class:`str` + """ + return self.BaseDllName.str.lower() + + @property + def fullname(self): + """Full name of the module (path) + + :type: :class:`str` + """ + return self.FullDllName.str.lower() + + def __repr__(self): + return '<{0} "{1}" at {2}>'.format(self.__class__.__name__, self.name, hex(id(self))) + + @property + def pe(self): + """A PE representation of the module + + :type: :class:`windows.pe_parse.PEFile` + """ + return pe_parse.GetPEFile(self.baseaddr)
+ + +class LIST_ENTRY_PTR(PVOID): + def TO_LDR_ENTRY(self): + return LDR_DATA_TABLE_ENTRY.from_address(self.value - sizeof(PVOID) * 2) + + +class RTL_USER_PROCESS_PARAMETERS(Structure): + _fields_ = transform_ctypes_fields(RTL_USER_PROCESS_PARAMETERS, # The one in generated_def + {"ImagePathName": WinUnicodeString, + "CommandLine": WinUnicodeString} + ) + + +
[docs]class PEB(Structure): + """The PEB (Process Environment Block) of the current process""" + _fields_ = transform_ctypes_fields(PEB, # The one in generated_def + {"ProcessParameters": POINTER(RTL_USER_PROCESS_PARAMETERS)} + ) + + @property + def imagepath(self): + """The ImagePathName of the PEB + + :type: :class:`WinUnicodeString` + """ + return self.ProcessParameters.contents.ImagePathName + + @property + def commandline(self): + """The CommandLine of the PEB + + :type: :class:`WinUnicodeString` + """ + # This or changing the __repr__ of LSA_UNICODE_STRING + return self.ProcessParameters.contents.CommandLine + + @property + def modules(self): + """The loaded modules present in the PEB + + :type: [:class:`LoadedModule`] -- List of loaded modules + """ + res = [] + list_entry_ptr = ctypes.cast(self.Ldr.contents.InMemoryOrderModuleList.Flink, LIST_ENTRY_PTR) + current_dll = list_entry_ptr.TO_LDR_ENTRY() + while current_dll.DllBase: + res.append(current_dll) + list_entry_ptr = ctypes.cast(current_dll.InMemoryOrderLinks.Flink, LIST_ENTRY_PTR) + current_dll = list_entry_ptr.TO_LDR_ENTRY() + return [LoadedModule.from_address(addressof(LDR)) for LDR in res]
+ + +# Memory stuff + +class EPSAPI_WORKING_SET_BLOCK_BASE(object): + @property + def protection(self): + return self.Flags & 0b11111 + + @property + def sharecount(self): + return (self.Flags >> 5) & 0b111 + + @property + def shared(self): + return (self.Flags >> 8) & 1 + + @property + def virtualpage(self): + return (self.Flags >> 12) + + +class EPSAPI_WORKING_SET_BLOCK(EPSAPI_WORKING_SET_BLOCK_BASE, PSAPI_WORKING_SET_BLOCK): + pass + +class EPSAPI_WORKING_SET_BLOCK32(EPSAPI_WORKING_SET_BLOCK_BASE, PSAPI_WORKING_SET_BLOCK32): + pass + +class EPSAPI_WORKING_SET_BLOCK64(EPSAPI_WORKING_SET_BLOCK_BASE, PSAPI_WORKING_SET_BLOCK64): + pass + + +class EPSAPI_WORKING_SET_EX_BLOCK_BASE(object): + @property + def valid(self): + return self.Flags & 0b1 + + @property + def sharecount(self): + return (self.Flags >> 1) & 0b111 + + @property + def shared(self): + return (self.Flags >> 15) & 1 + +class EPSAPI_WORKING_SET_EX_BLOCK(EPSAPI_WORKING_SET_EX_BLOCK_BASE, PSAPI_WORKING_SET_EX_BLOCK): + pass + +class EPSAPI_WORKING_SET_EX_BLOCK32(EPSAPI_WORKING_SET_EX_BLOCK_BASE, PSAPI_WORKING_SET_EX_BLOCK32): + pass + +class EPSAPI_WORKING_SET_EX_BLOCK64(EPSAPI_WORKING_SET_EX_BLOCK_BASE, PSAPI_WORKING_SET_EX_BLOCK64): + pass + +class EPSAPI_WORKING_SET_EX_INFORMATION(ctypes.Structure): + _fields_ = windows.utils.transform_ctypes_fields(PSAPI_WORKING_SET_EX_INFORMATION, {"VirtualAttributes": EPSAPI_WORKING_SET_EX_BLOCK}) + +class EPSAPI_WORKING_SET_EX_INFORMATION32(ctypes.Structure): + _fields_ = windows.utils.transform_ctypes_fields(PSAPI_WORKING_SET_EX_INFORMATION32, {"VirtualAttributes": EPSAPI_WORKING_SET_EX_BLOCK32}) + +class EPSAPI_WORKING_SET_EX_INFORMATION64(ctypes.Structure): + _fields_ = windows.utils.transform_ctypes_fields(PSAPI_WORKING_SET_EX_INFORMATION64, {"VirtualAttributes": EPSAPI_WORKING_SET_EX_BLOCK64}) + + +import windows.remotectypes as rctypes + + +class RemoteLoadedModule(rctypes.RemoteStructure.from_structure(LoadedModule)): + @property + def pe(self): + """A PE representation of the module + + :type: :class:`windows.pe_parse.PEFile` + """ + return pe_parse.GetPEFile(self.baseaddr, target=self._target) + + +class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)): + def ptr_flink_to_remote_module(self, ptr_value): + return RemoteLoadedModule(ptr_value - ctypes.sizeof(ctypes.c_void_p) * 2, self._target) + + @property + def modules(self): + """The loaded modules present in the PEB + + :type: [:class:`LoadedModule`] -- List of loaded modules + """ + res = [] + if not self.Ldr.value: + raise ValueError("PEB->Ldr is NULL: cannot walk the module list") + list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + while current_dll.DllBase: + res.append(current_dll) + list_entry_ptr = current_dll.InMemoryOrderLinks.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + return res + + +if CurrentProcess().bitness == 32: + class RemoteLoadedModule64(rctypes.transform_type_to_remote64bits(LoadedModule)): + @property + def pe(self): + """A PE representation of the module + + :type: :class:`windows.pe_parse.PEFile` + """ + return pe_parse.GetPEFile(self.baseaddr, target=self._target) + + class RemotePEB64(rctypes.transform_type_to_remote64bits(PEB)): + + def ptr_flink_to_remote_module(self, ptr_value): + return RemoteLoadedModule64(ptr_value - ctypes.sizeof(rctypes.c_void_p64) * 2, self._target) + + @property + def modules(self): + """The loaded modules present in the PEB + + :type: [:class:`LoadedModule`] -- List of loaded modules + """ + res = [] + if not self.Ldr.value: + raise ValueError("PEB->Ldr is NULL: cannot walk the module list") + list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + while current_dll.DllBase: + res.append(current_dll) + list_entry_ptr = current_dll.InMemoryOrderLinks.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + return res + +if CurrentProcess().bitness == 64: + + class RemoteLoadedModule32(rctypes.transform_type_to_remote32bits(LoadedModule)): + @property + def pe(self): + """A PE representation of the module + + :type: :class:`windows.pe_parse.PEFile` + """ + return pe_parse.GetPEFile(self.baseaddr, target=self._target) + + class RemotePEB32(rctypes.transform_type_to_remote32bits(PEB)): + def ptr_flink_to_remote_module(self, ptr_value): + return RemoteLoadedModule32(ptr_value - ctypes.sizeof(rctypes.c_void_p32) * 2, self._target) + + @property + def modules(self): + """The loaded modules present in the PEB + + :type: [:class:`LoadedModule`] -- List of loaded modules + """ + res = [] + if not self.Ldr.value: + raise ValueError("PEB->Ldr is NULL: cannot walk the module list") + list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + while current_dll.DllBase: + res.append(current_dll) + list_entry_ptr = current_dll.InMemoryOrderLinks.Flink.raw_value + current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) + return res +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/registry.html b/docs/build/html/_modules/windows/winobject/registry.html new file mode 100644 index 0000000..865fd07 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/registry.html @@ -0,0 +1,273 @@ + + + + + + + + windows.winobject.registry — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.registry

+import _winreg
+import itertools
+import struct
+from collections import namedtuple
+
+import windows
+from windows.generated_def.windef import KEY_READ, REG_QWORD
+
+
+
+class ExpectWindowsError(object):
+    def __init__(self, errornumber):
+        self.errornumber = errornumber
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, etype, e, tb):
+        return (etype == WindowsError and e.winerror == self.errornumber)
+
+
+KeyValue = namedtuple("KeyValue", ["name", "value", "type"])
+"""A registry value (name, value, type)"""
+
+
+
[docs]class PyHKey(object): + """A windows registry key""" + def __init__(self, surkey, name, sam=KEY_READ): + self.surkey = surkey + self.name = name + self.fullname = self.surkey.fullname + "\\" + self.name if self.name else self.surkey.name + self.sam = sam + self._phkey = None + #self.phkey + + def __repr__(self): + return '<PyHKey "{0}">'.format(self.fullname) + + @property + def phkey(self): + if self._phkey is not None: + return self._phkey + try: + self._phkey = _winreg.OpenKeyEx(self.surkey.phkey, self.name, 0, self.sam) + except WindowsError as e: + raise WindowsError("Could not open registry key <{0}>".format(self.fullname)) + return self._phkey + + + @property + def subkeys(self): + """The subkeys of the registry key + + :type: [:class:`PyHKey`] - A list of keys""" + res = [] + with ExpectWindowsError(259): + for i in itertools.count(): + res.append(_winreg.EnumKey(self.phkey, i)) + return [PyHKey(self, n) for n in res] + + @property + def values(self): + """The values of the registry key + + :type: [:class:`KeyValue`] - A list of values""" + res = [] + with ExpectWindowsError(259): + for i in itertools.count(): + name_value_type = _winreg.EnumValue(self.phkey, i) + # _winreg doest not support REG_QWORD + # See http://bugs.python.org/issue23026 + if name_value_type[2] == REG_QWORD: + name = name_value_type[0] + value = struct.unpack("<Q", name_value_type[1])[0] + type = name_value_type[2] + name_value_type = name, value, type + res.append(name_value_type) + return [KeyValue(*r) for r in res] + + @property + def info(self): + return _winreg.QueryInfoKey(self.phkey) + + @property + def last_write(self): + return self.info[2] + +
[docs] def get(self, value_name): + """Retrieves the value ``value_name`` + + :rtype: :class:`KeyValue` + """ + value, type = _winreg.QueryValueEx(self.phkey, value_name) + if type == REG_QWORD: + value = struct.unpack("<Q", value)[0] + return KeyValue(value_name, value, type)
+ + def _guess_value_type(self, value): + if isinstance(value, basestring): + return _winreg.REG_SZ + elif isinstance(value, (int, long)): + return _winreg.REG_DWORD + raise ValueError("Cannot guest registry type of value to set <{0}>".format(value)) + + +
[docs] def set(self, name, value, type=None): + """Set the value for ``name`` to ``value``. if ``type`` is None try to guess items""" + if type is None: + type = self._guess_value_type(value) + if type == REG_QWORD: + value = struct.pack("<Q", value) + return _winreg.SetValueEx(self.phkey, name, 0, type, value)
+ + +
[docs] def open_subkey(self, name, sam=None): + """Open the subkey ``name`` + + :rtype: :class:`PyHKey` + """ + if sam is None: + sam = self.sam + return PyHKey(self, name, sam)
+ + def reopen(self, new_sam): + return PyHKey(self.surkey, self.name, new_sam) + + +
[docs] def __setitem__(self, name, value): + rtype = None + if not isinstance(value, (int, long, basestring)): + value, rtype = value + return self.set(name, value, rtype)
+ + __getitem__ = get + + __call__ = open_subkey
+ + +class DummyPHKEY(object): + def __init__(self, phkey, name): + self.phkey = phkey + self.name = name + + +HKEY_LOCAL_MACHINE = PyHKey(DummyPHKEY(_winreg.HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE"), "", _winreg.KEY_READ) +HKEY_CLASSES_ROOT = PyHKey(DummyPHKEY(_winreg.HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT"), "", _winreg.KEY_READ ) +HKEY_CURRENT_USER = PyHKey(DummyPHKEY(_winreg.HKEY_CURRENT_USER, "HKEY_CURRENT_USER"), "", _winreg.KEY_READ) +HKEY_DYN_DATA = PyHKey(DummyPHKEY(_winreg.HKEY_DYN_DATA, "HKEY_DYN_DATA"), "", _winreg.KEY_READ) +HKEY_PERFORMANCE_DATA = PyHKey(DummyPHKEY(_winreg.HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA"), "", _winreg.KEY_READ) +HKEY_USERS = PyHKey(DummyPHKEY(_winreg.HKEY_USERS, "HKEY_USERS"), "", _winreg.KEY_READ ) + + +
[docs]class Registry(object): + """The ``Windows`` registry: a read only (for now) mapping""" + + registry_base_keys = { + "HKEY_LOCAL_MACHINE" : HKEY_LOCAL_MACHINE, + "HKEY_CLASSES_ROOT" : HKEY_CLASSES_ROOT, + "HKEY_CURRENT_USER" : HKEY_CURRENT_USER, + "HKEY_DYN_DATA" : HKEY_DYN_DATA, + "HKEY_PERFORMANCE_DATA": HKEY_PERFORMANCE_DATA, + "HKEY_USERS" : HKEY_USERS + } + + def __call__(self, name, sam=KEY_READ): + """Get a registry key:: + + registry[r"HKEY_LOCAL_MACHINE\\Software"] + registry["HKEY_LOCAL_MACHINE"]["Software"] + + :rtype: :class:`PyHKey` + """ + + if name in self.registry_base_keys: + key = self.registry_base_keys[name] + if sam != key.sam: + key = key.reopen(sam) + return key + if "\\" not in name: + raise ValueError("Unknow registry base key <{0}>".format(name)) + base_name, subkey = name.split("\\", 1) + if base_name not in self.registry_base_keys: + raise ValueError("Unknow registry base key <{0}>".format(base_name)) + return self.registry_base_keys[base_name](subkey, sam)
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/service.html b/docs/build/html/_modules/windows/winobject/service.html new file mode 100644 index 0000000..b22f8cc --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/service.html @@ -0,0 +1,205 @@ + + + + + + + + windows.winobject.service — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.service

+import ctypes
+import windows
+
+from collections import namedtuple
+
+from windows import utils
+from windows.generated_def import *
+
+
+SERVICE_TYPE = {x:x for x in [SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER, SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS]}
+SERVICE_STATE = {x:x for x in [SERVICE_STOPPED, SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, SERVICE_CONTINUE_PENDING, SERVICE_PAUSE_PENDING, SERVICE_PAUSED]}
+SERVICE_CONTROLE_ACCEPTED = {x:x for x in []}
+SERVICE_FLAGS = {x:x for x in [SERVICE_RUNS_IN_SYSTEM_PROCESS]}
+
+
+ServiceStatus = namedtuple("ServiceStatus", ["type", "state", "control_accepted", "flags"])
+"""
+``type`` might be one of:
+
+    * ``SERVICE_KERNEL_DRIVER(0x1L)``
+    * ``SERVICE_FILE_SYSTEM_DRIVER(0x2L)``
+    * ``SERVICE_WIN32_OWN_PROCESS(0x10L)``
+    * ``SERVICE_WIN32_SHARE_PROCESS(0x20L)``
+    * ``SERVICE_INTERACTIVE_PROCESS(0x100L)``
+
+``state`` might be one of:
+
+    * ``SERVICE_STOPPED(0x1L)``
+    * ``SERVICE_START_PENDING(0x2L)``
+    * ``SERVICE_STOP_PENDING(0x3L)``
+    * ``SERVICE_RUNNING(0x4L)``
+    * ``SERVICE_CONTINUE_PENDING(0x5L)``
+    * ``SERVICE_PAUSE_PENDING(0x6L)``
+    * ``SERVICE_PAUSED(0x7L)``
+
+``flags`` might be one of:
+
+    * ``0``
+    * ``SERVICE_RUNS_IN_SYSTEM_PROCESS(0x1L)``
+
+"""
+
+class Service(object):
+    def __repr__(self):
+        return '<{0} "{1}">'.format(type(self).__name__, self.name)
+
+    @utils.fixedpropety
+    def name(self):
+        """The name of the service
+
+        :type: :class:`str`
+        """
+        return self.lpServiceName
+
+    @utils.fixedpropety
+    def description(self):
+        """The description of the service
+
+        :type: :class:`str`
+        """
+        return self.lpDisplayName
+
+    @utils.fixedpropety
+    def status(self):
+        """The status of the service
+
+        :type: :class:`ServiceStatus`
+        """
+        status = self.ServiceStatusProcess
+        stype = SERVICE_TYPE.get(status.dwServiceType, status.dwServiceType)
+        sstate = SERVICE_STATE.get(status.dwCurrentState, status.dwCurrentState)
+        scontrol = status.dwControlsAccepted
+        sflags = SERVICE_FLAGS.get(status.dwServiceFlags, status.dwServiceFlags)
+        return ServiceStatus(stype, sstate, scontrol, sflags)
+
+    @utils.fixedpropety
+    def process(self):
+        """The process running the service (if any)
+
+        :type: :class:`WinProcess <windows.winobject.process.WinProcess>` or ``None``
+        """
+        pid = self.ServiceStatusProcess.dwProcessId
+        if not pid:
+            return None
+        l = [p for p in windows.system.processes if p.pid == pid]
+        if not l:
+            return None # Other thing ?
+        return l[0]
+
+
+
[docs]class ServiceA(Service, ENUM_SERVICE_STATUS_PROCESSA): + """A Service object with ascii data""" + pass
+ +def enumerate_services(): + scmanager = windows.winproxy.OpenSCManagerA(dwDesiredAccess=SC_MANAGER_ENUMERATE_SERVICE) + + size_needed = DWORD() + nb_services = DWORD() + counter = DWORD() + try: + windows.winproxy.EnumServicesStatusExA(scmanager, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_ACTIVE, None, 0, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None) + except WindowsError: + pass + + while True: + size = size_needed.value + buffer = (ctypes.c_byte * size)() + + try: + windows.winproxy.EnumServicesStatusExA(scmanager, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_ACTIVE, buffer, size, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None) + except WindowsError as e: + continue + + return_type = (ServiceA * nb_services.value) + return list(return_type.from_buffer(buffer)) +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/system.html b/docs/build/html/_modules/windows/winobject/system.html new file mode 100644 index 0000000..eaa4e93 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/system.html @@ -0,0 +1,314 @@ + + + + + + + + windows.winobject.system — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.system

+import os
+import ctypes
+import copy
+import struct
+
+import windows
+from windows import winproxy
+from windows import utils
+from windows.generated_def import windef
+
+from windows.winobject import process
+from windows.winobject import network
+from windows.winobject import registry
+from windows.winobject import exception
+from windows.winobject import service
+from windows.winobject import volume
+from windows.winobject import wmi
+from windows.winobject import kernobj
+from windows.winobject import handle
+
+from windows.generated_def.winstructs import *
+
+
[docs]class System(object): + """The state of the current ``Windows`` system ``Python`` is running on""" + + network = network.Network() + """Object of class :class:`windows.winobject.network.Network`""" + registry = registry.Registry() + """Object of class :class:`windows.winobject.registry.Registry`""" + + @property + def processes(self): + """The list of running processes + + :type: [:class:`process.WinProcess`] -- A list of Process + """ + return self.enumerate_processes() + + @property + def threads(self): + """The list of running threads + + :type: [:class:`process.WinThread`] -- A list of Thread + """ + return self.enumerate_threads() + + @property + def logicaldrives(self): + """List of logical drives [C:\, ...] + + :type: [:class:`volume.LogicalDrive`] -- A list of LogicalDrive + """ + return volume.enum_logical_drive() + + @property + def services(self): + """The list of services + + :type: [:class:`service.ServiceA`] -- A list of Service""" + return service.enumerate_services() + + @property + def handles(self): + """The list of system handles + + :type: [:class:`handle.Handle`] -- A list of Hanlde""" + return handle.enumerate_handles() + + @utils.fixedpropety + def bitness(self): + """The bitness of the system + + :type: :class:`int` -- 32 or 64 + """ + if os.environ["PROCESSOR_ARCHITECTURE"].lower() != "x86": + return 64 + if "PROCESSOR_ARCHITEW6432" in os.environ: + return 64 + return 32 + + @utils.fixedpropety + def wmi(self): + r"""An object to perform wmi request to "root\\cimv2" + + :type: :class:`windows.winobject.wmi.WmiRequester`""" + return wmi.WmiRequester() + + #TODO: use GetComputerNameExA ? and recover other names ? + @utils.fixedpropety + def computer_name(self): + """The name of the computer + + :type: :class:`str` + """ + size = DWORD(0x1000) + buf = ctypes.c_buffer(size.value) + winproxy.GetComputerNameA(buf, ctypes.byref(size)) + return buf[:size.value] + + @utils.fixedpropety + def version(self): + """The version of the system + + :type: (:class:`int`, :class:`int`) -- (Major, Minor) + """ + data = self.get_version() + result = data.dwMajorVersion, data.dwMinorVersion + if result == (6,2): + result_str = self.get_file_version("kernel32") + result_tup = [int(x) for x in result_str.split(".")] + result = tuple(result_tup[:2]) + return result + + @utils.fixedpropety + def version_name(self): + """The name of the system version, values are: + + * Windows Server 2016 + * Windows 10 + * Windows Server 2012 R2 + * Windows 8.1 + * Windows Server 2012 + * Windows 8 + * Windows Server 2008 + * Windows 7 + * Windows Server 2008 + * Windows Vista + * Windows XP Professional x64 Edition + * TODO: version (5.2) + is_workstation + bitness == 32 (don't even know if possible..) + * Windows Server 2003 R2 + * Windows Server 2003 + * Windows XP + * Windows 2000 + * "Unknow Windows <version={0} | is_workstation={1}>".format(version, is_workstation) + + :type: :class:`str` + """ + version = self.version + is_workstation = self.product_type == VER_NT_WORKSTATION + if version == (10, 0): + return ["Windows Server 2016", "Windows 10"][is_workstation] + elif version == (6, 3): + return ["Windows Server 2012 R2", "Windows 8.1"][is_workstation] + elif version == (6, 2): + return ["Windows Server 2012", "Windows 8"][is_workstation] + elif version == (6, 1): + return ["Windows Server 2008 R2", "Windows 7"][is_workstation] + elif version == (6, 0): + return ["Windows Server 2008", "Windows Vista"][is_workstation] + elif version == (5, 2): + metric = winproxy.GetSystemMetrics(SM_SERVERR2) + if is_workstation: + if self.bitness == 64: + return "Windows XP Professional x64 Edition" + else: + return "TODO: version (5.2) + is_workstation + bitness == 32" + elif metric != 0: + return "Windows Server 2003 R2" + else: + return "Windows Server 2003" + elif version == (5, 1): + return "Windows XP" + elif version == (5, 0): + return "Windows 2000" + else: + return "Unknow Windows <version={0} | is_workstation={1}>".format(version, is_workstation) + + @utils.fixedpropety + def product_type(self): + """The product type, value might be: + + * VER_NT_WORKSTATION(0x1L) + * VER_NT_DOMAIN_CONTROLLER(0x2L) + * VER_NT_SERVER(0x3L) + + :type: :class:`long` or :class:`int` (or subclass) + """ + version_map = {x:x for x in [VER_NT_WORKSTATION, VER_NT_DOMAIN_CONTROLLER, VER_NT_SERVER]} + version = self.get_version() + return version_map.get(version.wProductType, version.wProductType) + + def get_version(self): + data = windows.generated_def.OSVERSIONINFOEXA() + data.dwOSVersionInfoSize = ctypes.sizeof(data) + winproxy.GetVersionExA(ctypes.cast(ctypes.pointer(data), ctypes.POINTER(windows.generated_def.OSVERSIONINFOA))) + return data + + def get_file_version(self, name): + size = winproxy.GetFileVersionInfoSizeA(name) + buf = ctypes.c_buffer(size) + winproxy.GetFileVersionInfoA(name, 0, size, buf) + + bufptr = PVOID() + bufsize = UINT() + winproxy.VerQueryValueA(buf, "\\VarFileInfo\\Translation", ctypes.byref(bufptr), ctypes.byref(bufsize)) + bufstr = ctypes.cast(bufptr, LPCSTR) + tup = struct.unpack("<HH", bufstr.value[:4]) + req = "{0:04x}{1:04x}".format(*tup) + winproxy.VerQueryValueA(buf, "\\StringFileInfo\\{0}\\ProductVersion".format(req), ctypes.byref(bufptr), ctypes.byref(bufsize)) + bufstr = ctypes.cast(bufptr, LPCSTR) + return bufstr.value + + @staticmethod + def enumerate_processes(): + process_entry = PROCESSENTRY32() + process_entry.dwSize = ctypes.sizeof(process_entry) + snap = winproxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPPROCESS, 0) + winproxy.Process32First(snap, process_entry) + res = [] + res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) + while winproxy.Process32Next(snap, process_entry): + res.append(process.WinProcess._from_PROCESSENTRY32(process_entry)) + return res + + @staticmethod + def enumerate_threads(): + thread_entry = process.WinThread() + thread_entry.dwSize = ctypes.sizeof(thread_entry) + snap = winproxy.CreateToolhelp32Snapshot(windef.TH32CS_SNAPTHREAD, 0) + threads = [] + winproxy.Thread32First(snap, thread_entry) + threads.append(copy.copy(thread_entry)) + while winproxy.Thread32Next(snap, thread_entry): + threads.append(copy.copy(thread_entry)) + return threads
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/volume.html b/docs/build/html/_modules/windows/winobject/volume.html new file mode 100644 index 0000000..61dc34e --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/volume.html @@ -0,0 +1,160 @@ + + + + + + + + windows.winobject.volume — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.volume

+import ctypes
+
+import windows
+from windows import winproxy
+from windows.generated_def.winstructs import *
+
+
+
+
+
[docs]class LogicalDrive(object): + DRIVE_TYPE = {x:x for x in [DRIVE_UNKNOWN, DRIVE_NO_ROOT_DIR, DRIVE_REMOVABLE, + DRIVE_FIXED, DRIVE_REMOTE, DRIVE_CDROM, DRIVE_RAMDISK]} + + def __init__(self, name): + self.name = name + + @property + def type(self): + """The type of drive, values are: + + * DRIVE_UNKNOWN(0x0L) + * DRIVE_NO_ROOT_DIR(0x1L) + * DRIVE_REMOVABLE(0x2L) + * DRIVE_FIXED(0x3L) + * DRIVE_REMOTE(0x4L) + * DRIVE_CDROM(0x5L) + * DRIVE_RAMDISK(0x6L) + + :type: :class:`long` or :class:`int` (or subclass) + """ + t = winproxy.GetDriveTypeA(self.name) + return self.DRIVE_TYPE.get(t,t) + + @property + def path(self): + """The target path of the device + + :type: :class:`str`""" + res = query_dos_device(self.name.strip("\\")) + if len(res) != 1: + raise ValueError("[Unexpected result] query_dos_device(logicaldrive) returned multiple path") + return res[0] + + + def __repr__(self): + return """<{0} "{1}" ({2})>""".format(type(self).__name__, self.name, self.type.name)
+ + + +def enum_logical_drive(): + return [LogicalDrive(name) for name in get_logical_drive_names()] + +def get_logical_drive_names(): + size = 0x100 + buffer = ctypes.c_buffer(size) + rsize = winproxy.GetLogicalDriveStringsA(0x1000, buffer) + return buffer[:rsize].rstrip("\x00").split("\x00") + +def get_info(drivename): + size = 0x1000 + volume_name = ctypes.c_buffer(size) + fs_name = ctypes.c_buffer(size) + flags = DWORD() + winproxy.GetVolumeInformationA(drivename, volume_name, size, None, None, ctypes.byref(flags), fs_name, size) + raise NotImplementedError("get_info") + +def query_dos_device(name): + size = 0x1000 + buffer = ctypes.c_buffer(size) + rsize = winproxy.QueryDosDeviceA(name, buffer, size) + return buffer[:rsize].rstrip("\x00").split("\x00") +
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/winobject/wmi.html b/docs/build/html/_modules/windows/winobject/wmi.html new file mode 100644 index 0000000..dadf691 --- /dev/null +++ b/docs/build/html/_modules/windows/winobject/wmi.html @@ -0,0 +1,178 @@ + + + + + + + + windows.winobject.wmi — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.winobject.wmi

+import windows
+import ctypes
+import struct
+import functools
+
+from ctypes.wintypes import *
+
+import windows.com
+from windows.generated_def.winstructs import *
+from windows.generated_def.interfaces import IWbemLocator, IWbemServices, IEnumWbemClassObject, IWbemClassObject
+
+
+
[docs]class WmiRequester(object): + r"""An object to perform wmi request to ``root\cimv2``""" + INSTANCE = None + + def __new__(cls, *args, **kwargs): + if cls.INSTANCE is not None: + return cls.INSTANCE + cls.INSTANCE = super(cls, cls).__new__(cls, *args, **kwargs) + return cls.INSTANCE + + def __init__(self, target="root\\cimv2", user=None, password=None): + locator = IWbemLocator() + service = IWbemServices() + #CLSID_WbemAdministrativeLocator_IID = windows.com.IID.from_string('CB8555CC-9128-11D1-AD9B-00C04FD8FDFF') + WbemLocator_CLSID = windows.com.IID.from_string('4590F811-1D3A-11D0-891F-00AA004B2E24') + + windows.com.init() + windows.com.create_instance(WbemLocator_CLSID, locator) + locator.ConnectServer(target, user, password , None, 0x80, None, None, ctypes.byref(service)) + self.service = service + +
[docs] def select(self, frm, attrs="*"): + """Select ``attrs`` from ``frm`` + + :rtype: list of dict + """ + enumerator = IEnumWbemClassObject() + try: + self.service.ExecQuery("WQL", "select * from {0}".format(frm), 0x20, 0, ctypes.byref(enumerator)) + except WindowsError as e: + if (e.winerror & 0xffffffff) == WBEM_E_INVALID_CLASS: + raise WindowsError(e.winerror, 'WBEM_E_INVALID_CLASS <Invalid WMI class "{0}">'.format(frm)) + elif (e.winerror & 0xffffffff) in WBEMSTATUS.values: + raise WindowsError(e.winerror, WBEMSTATUS(e.winerror & 0xffffffff).value) + raise + + count = ctypes.c_ulong(0) + processor = IWbemClassObject() + res = [] + enumerator.Next(0xffffffff, 1, ctypes.byref(processor), ctypes.byref(count)) + while count.value: + current_res = {} + variant_res = windows.com.ImprovedVariant() + if attrs == "*": + attrs = [x for x in self.get_names(processor) if not x.startswith("__")] + for name in attrs: + processor.Get(name, 0, ctypes.byref(variant_res), None, None) + # TODO: something clean and generic + if variant_res.vt & VT_ARRAY: + if variant_res.vt & VT_TYPEMASK == VT_BSTR: + current_res[name] = variant_res.asarray.to_list(BSTR) + if variant_res.vt & VT_TYPEMASK == VT_I4: + current_res[name] = variant_res.asarray.to_list(LONG) + elif variant_res.vt in [VT_EMPTY, VT_NULL]: + current_res[name] = None + elif variant_res.vt == VT_BSTR: + current_res[name] = variant_res.asbstr + elif variant_res.vt == VT_I4: + current_res[name] = variant_res.aslong + elif variant_res.vt == VT_BOOL: + current_res[name] = variant_res.asbool + elif variant_res.vt == VT_I2: + current_res[name] = variant_res.asshort + elif variant_res.vt == VT_UI1: + current_res[name] = variant_res.asbyte + else: + print("[WARN] WMI Ignore variant of type {0}".format(hex(variant_res.vt))) + res.append(current_res) + enumerator.Next(0xffffffff, 1, ctypes.byref(processor), ctypes.byref(count)) + return res
+ + def get_names(self, processor): + res = POINTER(SAFEARRAY)() + processor.GetNames(None, 0, None, byref(res)) + safe_array = ctypes.cast(res, POINTER(windows.com.ImprovedSAFEARRAY))[0] + safe_array.elt_type = BSTR + return safe_array.to_list()
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/docs/build/html/_modules/windows/wintrust.html b/docs/build/html/_modules/windows/wintrust.html new file mode 100644 index 0000000..d9d0636 --- /dev/null +++ b/docs/build/html/_modules/windows/wintrust.html @@ -0,0 +1,285 @@ + + + + + + + + windows.wintrust — PythonForWindows 0.2 documentation + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for windows.wintrust

+import ctypes
+import struct
+import windows
+from  collections import namedtuple
+from windows import winproxy
+from windows.generated_def.winstructs import *
+
+
+IID_PACK = "<I", "<H", "<H", "<B", "<B", "<B", "<B", "<B", "<B", "<B", "<B"
+def get_IID_from_raw(raw):
+    s = "".join([struct.pack(i, j) for i, j in zip(IID_PACK, raw)])
+    return ctypes.create_string_buffer(s)
+
+
+WINTRUST_ACTION_GENERIC_VERIFY_V2_RAW = (0xaac56b, 0xcd44,  0x11d0,
+                    0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee)
+
+WINTRUST_ACTION_GENERIC_VERIFY_V2_STR = get_IID_from_raw(WINTRUST_ACTION_GENERIC_VERIFY_V2_RAW)
+# Otherwise there is a problem with `Data4` of `type c_char_Array_8` containing 0x00 (0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee)
+WINTRUST_ACTION_GENERIC_VERIFY_V2 = GUID.from_address(ctypes.addressof(WINTRUST_ACTION_GENERIC_VERIFY_V2_STR))
+
+DRIVER_ACTION_VERIFY_RAW = 0xf750e6c3, 0x38ee, 0x11d1, 0x85, 0xe5, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee
+DRIVER_ACTION_VERIFY_STR = get_IID_from_raw(DRIVER_ACTION_VERIFY_RAW)
+DRIVER_ACTION_VERIFY = GUID.from_address(ctypes.addressof(DRIVER_ACTION_VERIFY_STR))
+
+WTD_UI_ALL    = 1
+WTD_UI_NONE   = 2
+WTD_UI_NOBAD  = 3
+WTD_UI_NOGOOD = 4
+
+WTD_REVOKE_NONE         = 0x00000000
+WTD_REVOKE_WHOLECHAIN   = 0x00000001
+
+WTD_CHOICE_FILE    = 1
+WTD_CHOICE_CATALOG = 2
+WTD_CHOICE_BLOB    = 3
+WTD_CHOICE_SIGNER  = 4
+WTD_CHOICE_CERT    = 5
+
+WTD_STATEACTION_IGNORE           = 0x00000000
+WTD_STATEACTION_VERIFY           = 0x00000001
+WTD_STATEACTION_CLOSE            = 0x00000002
+WTD_STATEACTION_AUTO_CACHE       = 0x00000003
+WTD_STATEACTION_AUTO_CACHE_FLUSH = 0x00000004
+
+wintrust_know_return_value = [
+TRUST_E_PROVIDER_UNKNOWN,
+TRUST_E_ACTION_UNKNOWN,
+TRUST_E_SUBJECT_FORM_UNKNOWN,
+DIGSIG_E_ENCODE,
+TRUST_E_SUBJECT_NOT_TRUSTED,
+DIGSIG_E_DECODE,
+DIGSIG_E_EXTENSIBILITY,
+PERSIST_E_SIZEDEFINITE,
+DIGSIG_E_CRYPTO,
+PERSIST_E_SIZEINDEFINITE,
+PERSIST_E_NOTSELFSIZING,
+TRUST_E_NOSIGNATURE,
+CERT_E_EXPIRED,
+CERT_E_VALIDITYPERIODNESTING,
+CERT_E_PURPOSE,
+CERT_E_ISSUERCHAINING,
+CERT_E_MALFORMED,
+CERT_E_UNTRUSTEDROOT,
+CERT_E_CHAINING,
+TRUST_E_FAIL,
+CERT_E_REVOKED,
+CERT_E_UNTRUSTEDTESTROOT,
+CERT_E_REVOCATION_FAILURE,
+CERT_E_CN_NO_MATCH,
+CERT_E_WRONG_USAGE,
+TRUST_E_EXPLICIT_DISTRUST,
+CERT_E_UNTRUSTEDCA,
+CERT_E_INVALID_POLICY,
+CERT_E_INVALID_NAME,
+CRYPT_E_FILE_ERROR,
+]
+wintrust_return_value_mapper = {x:x for x in wintrust_know_return_value}
+
+
+
[docs]def check_signature(filename): + """Check if ``filename`` embeds a valid signature. + + :return: :class:`int`: ``0`` if ``filename`` have a valid signature else the error + """ + file_data = WINTRUST_FILE_INFO() + file_data.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO) + file_data.pcwszFilePath = filename + file_data.hFile = None + file_data.pgKnownSubject = None + + WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2 + + win_trust_data = WINTRUST_DATA() + win_trust_data.cbStruct = ctypes.sizeof(WINTRUST_DATA) + win_trust_data.pPolicyCallbackData = None + win_trust_data.pSIPClientData = None + win_trust_data.dwUIChoice = WTD_UI_NONE + win_trust_data.fdwRevocationChecks = WTD_REVOKE_NONE + win_trust_data.dwUnionChoice = WTD_CHOICE_FILE + win_trust_data.dwStateAction = WTD_STATEACTION_VERIFY + win_trust_data.hWVTStateData = None + win_trust_data.pwszURLReference = None + win_trust_data.dwUIContext = 0 + + #win_trust_data.dwProvFlags = 0x1000 + 0x10 + 0x800 + win_trust_data.tmp_union.pFile = ctypes.pointer(file_data) + + x = winproxy.WinVerifyTrust(None, ctypes.byref(WVTPolicyGUID), ctypes.byref(win_trust_data)) + win_trust_data.dwStateAction = WTD_STATEACTION_CLOSE + winproxy.WinVerifyTrust(None, ctypes.byref(WVTPolicyGUID), ctypes.byref(win_trust_data)) + return wintrust_return_value_mapper.get(x & 0xffffffff, x & 0xffffffff)
+ + +def get_catalog_for_filename(filename): + ctx = HCATADMIN() + winproxy.CryptCATAdminAcquireContext(ctypes.byref(ctx), DRIVER_ACTION_VERIFY, 0) + hash = get_file_hash(filename) + if hash is None: + return None + t = winproxy.CryptCATAdminEnumCatalogFromHash(ctx, hash, len(hash), 0, None) + if t is None: + return None + tname = get_catalog_name_from_handle(t) + + while t is not None: + t = winproxy.CryptCATAdminEnumCatalogFromHash(ctx, hash, len(hash), 0, ctypes.byref(HCATINFO(t))) + winproxy.CryptCATAdminReleaseCatalogContext(ctx, t, 0) + winproxy.CryptCATAdminReleaseContext(ctx, 0) + return tname + + +def get_file_hash(filename): + f = open(filename, "rb") + handle = windows.utils.get_handle_from_file(f) + + size = DWORD(0) + x = winproxy.CryptCATAdminCalcHashFromFileHandle(handle, ctypes.byref(size), None, 0) + buffer = (BYTE * size.value)() + try: + x = winproxy.CryptCATAdminCalcHashFromFileHandle(handle, ctypes.byref(size), buffer, 0) + except WindowsError as e: + if e.winerror == 1006: + # CryptCATAdminCalcHashFromFileHandle: [Error 1006] + # The volume for a file has been externally altered so that the opened file is no longer valid. + # (returned for empty file) + return None + return buffer + + +def get_catalog_name_from_handle(handle): + cat_info = CATALOG_INFO() + cat_info.cbStruct = ctypes.sizeof(cat_info) + winproxy.CryptCATCatalogInfoFromContext(handle, ctypes.byref(cat_info), 0) + return cat_info.wszCatalogFile + +SignatureData = namedtuple("SignatureData", ["signed", "catalog", "catalogsigned", "additionalinfo"]) +"""Signature information for ``FILENAME``: + + * ``signed``: True if ``FILENAME`` embeds a valide signature + * ``catalog``: The filename of the catalog ``FILENAME`` is part of (if any) + * ``catalogsigned``: True if ``catalog`` embeds a valide signature + * ``additionalinfo``: The return error of ``check_signature(FILENAME)`` + +``additionalinfo`` is useful to know if ``FILENAME`` signature was rejected for an invalid root / expired cert. +""" + +
[docs]def full_signature_information(filename): + """Returns more information about the signature of ``filename`` + + :return: :class:`SignatureData` + """ + check_sign = check_signature(filename) + signed = not bool(check_sign) + catalog = get_catalog_for_filename(filename) + if catalog is None: + return SignatureData(signed, None, False, check_sign) + catalogsigned = not bool(check_signature(catalog)) + return SignatureData(signed, catalog, catalogsigned, check_sign)
+ +
[docs]def is_signed(filename): + """Check if ``filename`` is signed: + + * File embeds a valid signature + * File is part of a signed catalog file + + :return: :class:`bool` + """ + check_sign = check_signature(filename) + if check_sign == 0: + return True + catalog = get_catalog_for_filename(filename) + if catalog is None: + return False + catalogsigned = not bool(check_signature(catalog)) + return catalogsigned
+
+ +
+
+
+ +
+
+ + + + \ No newline at end of file diff --git a/doc/source/com.rst b/docs/build/html/_sources/com.txt similarity index 100% rename from doc/source/com.rst rename to docs/build/html/_sources/com.txt diff --git a/doc/source/debug.rst b/docs/build/html/_sources/debug.txt similarity index 100% rename from doc/source/debug.rst rename to docs/build/html/_sources/debug.txt diff --git a/doc/source/exception.rst b/docs/build/html/_sources/exception.txt similarity index 100% rename from doc/source/exception.rst rename to docs/build/html/_sources/exception.txt diff --git a/doc/source/handle.rst b/docs/build/html/_sources/handle.txt similarity index 100% rename from doc/source/handle.rst rename to docs/build/html/_sources/handle.txt diff --git a/doc/source/iat_hook.rst b/docs/build/html/_sources/iat_hook.txt similarity index 100% rename from doc/source/iat_hook.rst rename to docs/build/html/_sources/iat_hook.txt diff --git a/doc/source/index.rst b/docs/build/html/_sources/index.txt similarity index 100% rename from doc/source/index.rst rename to docs/build/html/_sources/index.txt diff --git a/doc/source/internals.rst b/docs/build/html/_sources/internals.txt similarity index 100% rename from doc/source/internals.rst rename to docs/build/html/_sources/internals.txt diff --git a/doc/source/native_exec.rst b/docs/build/html/_sources/native_exec.txt similarity index 100% rename from doc/source/native_exec.rst rename to docs/build/html/_sources/native_exec.txt diff --git a/doc/source/network.rst b/docs/build/html/_sources/network.txt similarity index 100% rename from doc/source/network.rst rename to docs/build/html/_sources/network.txt diff --git a/doc/source/process.rst b/docs/build/html/_sources/process.txt similarity index 100% rename from doc/source/process.rst rename to docs/build/html/_sources/process.txt diff --git a/doc/source/registry.rst b/docs/build/html/_sources/registry.txt similarity index 100% rename from doc/source/registry.rst rename to docs/build/html/_sources/registry.txt diff --git a/doc/source/sample.rst b/docs/build/html/_sources/sample.txt similarity index 100% rename from doc/source/sample.rst rename to docs/build/html/_sources/sample.txt diff --git a/doc/source/service.rst b/docs/build/html/_sources/service.txt similarity index 100% rename from doc/source/service.rst rename to docs/build/html/_sources/service.txt diff --git a/doc/source/utils.rst b/docs/build/html/_sources/utils.txt similarity index 100% rename from doc/source/utils.rst rename to docs/build/html/_sources/utils.txt diff --git a/doc/source/various.rst b/docs/build/html/_sources/various.txt similarity index 100% rename from doc/source/various.rst rename to docs/build/html/_sources/various.txt diff --git a/doc/source/volume.rst b/docs/build/html/_sources/volume.txt similarity index 100% rename from doc/source/volume.rst rename to docs/build/html/_sources/volume.txt diff --git a/doc/source/windows.rst b/docs/build/html/_sources/windows.txt similarity index 100% rename from doc/source/windows.rst rename to docs/build/html/_sources/windows.txt diff --git a/doc/source/winproxy.rst b/docs/build/html/_sources/winproxy.txt similarity index 100% rename from doc/source/winproxy.rst rename to docs/build/html/_sources/winproxy.txt diff --git a/doc/source/wintrust.rst b/docs/build/html/_sources/wintrust.txt similarity index 100% rename from doc/source/wintrust.rst rename to docs/build/html/_sources/wintrust.txt diff --git a/doc/source/wip.rst b/docs/build/html/_sources/wip.txt similarity index 100% rename from doc/source/wip.rst rename to docs/build/html/_sources/wip.txt diff --git a/doc/source/wmi.rst b/docs/build/html/_sources/wmi.txt similarity index 100% rename from doc/source/wmi.rst rename to docs/build/html/_sources/wmi.txt diff --git a/docs/build/html/_static/ajax-loader.gif b/docs/build/html/_static/ajax-loader.gif new file mode 100644 index 0000000..61faf8c Binary files /dev/null and b/docs/build/html/_static/ajax-loader.gif differ diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css new file mode 100644 index 0000000..2b513f0 --- /dev/null +++ b/docs/build/html/_static/basic.css @@ -0,0 +1,604 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable dl, table.indextable dd { + margin-top: 0; + margin-bottom: 0; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.field-list ul { + padding-left: 1em; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.field-list td, table.field-list th { + border: 0 !important; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/build/html/_static/classic.css b/docs/build/html/_static/classic.css new file mode 100644 index 0000000..d98894b --- /dev/null +++ b/docs/build/html/_static/classic.css @@ -0,0 +1,261 @@ +/* + * default.css_t + * ~~~~~~~~~~~~~ + * + * Sphinx stylesheet -- default theme. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: sans-serif; + font-size: 100%; + background-color: #11303d; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + background-color: #1c4e63; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + background-color: #ffffff; + color: #000000; + padding: 0 20px 30px 20px; +} + +div.footer { + color: #ffffff; + width: 100%; + padding: 9px 0 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + color: #ffffff; + text-decoration: underline; +} + +div.related { + background-color: #133f52; + line-height: 30px; + color: #ffffff; +} + +div.related a { + color: #ffffff; +} + +div.sphinxsidebar { +} + +div.sphinxsidebar h3 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: #ffffff; +} + +div.sphinxsidebar h4 { + font-family: 'Trebuchet MS', sans-serif; + color: #ffffff; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: #ffffff; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: #ffffff; +} + +div.sphinxsidebar a { + color: #98dbcc; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + + + +/* -- hyperlink styles ------------------------------------------------------ */ + +a { + color: #355f7c; + text-decoration: none; +} + +a:visited { + color: #355f7c; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + + + +/* -- body styles ----------------------------------------------------------- */ + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Trebuchet MS', sans-serif; + background-color: #f2f2f2; + font-weight: normal; + color: #20435c; + border-bottom: 1px solid #ccc; + margin: 20px -20px 10px -20px; + padding: 3px 0 3px 10px; +} + +div.body h1 { margin-top: 0; font-size: 200%; } +div.body h2 { font-size: 160%; } +div.body h3 { font-size: 140%; } +div.body h4 { font-size: 120%; } +div.body h5 { font-size: 110%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #c60f0f; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: #c60f0f; + color: white; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + text-align: justify; + line-height: 130%; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p { + margin-bottom: 5px; +} + +div.admonition pre { + margin-bottom: 5px; +} + +div.admonition ul, div.admonition ol { + margin-bottom: 5px; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre { + padding: 5px; + background-color: #eeffcc; + color: #333333; + line-height: 120%; + border: 1px solid #ac9; + border-left: none; + border-right: none; +} + +code { + background-color: #ecf0f3; + padding: 0 1px 0 1px; + font-size: 0.95em; +} + +th { + background-color: #ede; +} + +.warning code { + background: #efc2c2; +} + +.note code { + background: #d6d6d6; +} + +.viewcode-back { + font-family: sans-serif; +} + +div.viewcode-block:target { + background-color: #f4debf; + border-top: 1px solid #ac9; + border-bottom: 1px solid #ac9; +} + +div.code-block-caption { + color: #efefef; + background-color: #1c4e63; +} \ No newline at end of file diff --git a/docs/build/html/_static/comment-bright.png b/docs/build/html/_static/comment-bright.png new file mode 100644 index 0000000..551517b Binary files /dev/null and b/docs/build/html/_static/comment-bright.png differ diff --git a/docs/build/html/_static/comment-close.png b/docs/build/html/_static/comment-close.png new file mode 100644 index 0000000..09b54be Binary files /dev/null and b/docs/build/html/_static/comment-close.png differ diff --git a/docs/build/html/_static/comment.png b/docs/build/html/_static/comment.png new file mode 100644 index 0000000..92feb52 Binary files /dev/null and b/docs/build/html/_static/comment.png differ diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js new file mode 100644 index 0000000..8163495 --- /dev/null +++ b/docs/build/html/_static/doctools.js @@ -0,0 +1,287 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); \ No newline at end of file diff --git a/docs/build/html/_static/down-pressed.png b/docs/build/html/_static/down-pressed.png new file mode 100644 index 0000000..7c30d00 Binary files /dev/null and b/docs/build/html/_static/down-pressed.png differ diff --git a/docs/build/html/_static/down.png b/docs/build/html/_static/down.png new file mode 100644 index 0000000..f48098a Binary files /dev/null and b/docs/build/html/_static/down.png differ diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png new file mode 100644 index 0000000..254c60b Binary files /dev/null and b/docs/build/html/_static/file.png differ diff --git a/docs/build/html/_static/jquery-1.11.1.js b/docs/build/html/_static/jquery-1.11.1.js new file mode 100644 index 0000000..d4b67f7 --- /dev/null +++ b/docs/build/html/_static/jquery-1.11.1.js @@ -0,0 +1,10308 @@ +/*! + * jQuery JavaScript Library v1.11.1 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-05-01T17:42Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var deletedIds = []; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.11.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( support.ownLast ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1, IE<9 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.19 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-04-18 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !(--remaining) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + + +var strundefined = typeof undefined; + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownLast = i !== "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery(function() { + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); +}); + + + + +(function() { + var div = document.createElement( "div" ); + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( elem ) { + var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute("classid") === noData; +}; + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[0], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; +}; +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + // Minified: var a,b,c + var input = document.createElement( "input" ), + div = document.createElement( "div" ), + fragment = document.createDocumentFragment(); + + // Setup + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + div.innerHTML = ""; + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + support.noCloneEvent = true; + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } +})(); + + +(function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) + for ( i in { submit: true, change: true, focusin: true }) { + eventName = "on" + i; + + if ( !(support[ i + "Bubbles" ] = eventName in window) ) { + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: IE < 9, Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!support.noCloneEvent || !support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + deletedIds.push( id ); + } + } + } + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "