From b2eede98b18482683090e6ca61efe49c8066bd04 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Tue, 5 Jan 2016 18:03:53 +0100 Subject: [PATCH] More doc (registry / network) --- doc/source/native_exec.rst | 3 --- doc/source/network.rst | 21 +++++++++++++++++ doc/source/process.rst | 18 ++++++++------ doc/source/registry.rst | 8 ++++--- doc/source/sample.rst | 47 +++++++++++++++++++++++++++++-------- doc/source/various.rst | 3 ++- samples/network.py | 42 +++++++++++++++++++++++++++++++++ windows/network.py | 48 ++++++++++++++++++++++++++++++++++++++ windows/registry.py | 10 ++++++++ windows/winproxy.py | 22 +++++++++++++---- 10 files changed, 193 insertions(+), 29 deletions(-) create mode 100644 doc/source/network.rst create mode 100644 samples/network.py diff --git a/doc/source/native_exec.rst b/doc/source/native_exec.rst index 01021a1..1a47d9b 100644 --- a/doc/source/native_exec.rst +++ b/doc/source/native_exec.rst @@ -139,9 +139,6 @@ Another example from a project:: out_ioctl += x86.Mov('EAX', 0x0C000000D) out_ioctl += x86.Ret() -.. note:: - - TODO: prefix :mod:`windows.native_exec.simple_x64` -- X64 Assembler """""""""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/doc/source/network.rst b/doc/source/network.rst new file mode 100644 index 0000000..abdbef4 --- /dev/null +++ b/doc/source/network.rst @@ -0,0 +1,21 @@ +Network +======= + +.. module:: windows.network + +.. note:: + + See sample :ref:`sample_network_exploration` + + +:class:`Network` class +"""""""""""""""""""""" + +.. autoclass:: windows.network.Network + +Connection classes +"""""""""""""""""" + +.. autoclass:: windows.network.TCP4Connection + +.. autoclass:: windows.network.TCP6Connection \ No newline at end of file diff --git a/doc/source/process.rst b/doc/source/process.rst index f9cf0bf..a8e1c8f 100644 --- a/doc/source/process.rst +++ b/doc/source/process.rst @@ -13,7 +13,7 @@ CurrentProcess .. autoclass:: CurrentProcess :members: :inherited-members: - + CurrentThread ''''''''''''' @@ -39,13 +39,13 @@ WinThread .. autoclass:: WinThread :members: :inherited-members: - + .. autoclass:: DeadThread :members: :inherited-members: - - + + PEB Exploration """"""""""""""" @@ -59,10 +59,14 @@ The :class:`PEB` is accessible via ``process.peb`` and is of type :class:`PEB`. .. autoclass:: PEB :members: :inherited-members: - + .. autoclass:: WinUnicodeString -.. autoclass:: LoadedModule - +.. autoclass:: LoadedModule + + +.. warning:: + + TODO: pe_parse.PEFile (sorry) but example at :ref:`sample_peb_exploration` diff --git a/doc/source/registry.rst b/doc/source/registry.rst index 2cba624..28e340b 100644 --- a/doc/source/registry.rst +++ b/doc/source/registry.rst @@ -1,8 +1,10 @@ Registry -"""""""" +======== .. module:: windows.registry -REGISTRY -.. class:: Registry \ No newline at end of file +.. autoclass:: Registry + :special-members: __getitem__ + +.. autoclass:: PyHKey \ No newline at end of file diff --git a/doc/source/sample.rst b/doc/source/sample.rst index 34f18c0..892a923 100644 --- a/doc/source/sample.rst +++ b/doc/source/sample.rst @@ -1,7 +1,7 @@ Samples of code =============== -.. _sample_current_process: +.. _sample_current_process: ``windows.current_process`` """"""""""""""""""""""""""" @@ -23,9 +23,9 @@ Output:: Allocated memory is at <0x3f0000> Writing 'SOME STUFF' in allocation memory Reading memory : <'SOME STUFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'> - -.. _sample_remote_process: - + +.. _sample_remote_process: + Remote process : :class:`WinProcess` """""""""""""""""""""""""""""""""""" @@ -61,12 +61,12 @@ Output:: File "", line 3, in File "", line 2, in func ImportError: No module named FAKE_MODULE - + That's all ! killing the calc - - -.. _sample_peb_exploration: - + + +.. _sample_peb_exploration: + :class:`PEB` exploration """""""""""""""""""""""" @@ -96,4 +96,31 @@ Output:: Here are some exports {0: 2001566688L, u'CreateFileA': 2001635616L, 42: 2001647872L, u'VirtualAlloc': 2001570704L} Import DLL dependancies are (without api-*): [u'ntdll.dll', u'kernelbase.dll'] IAT Entry for ntdll!NtCreateFile = | addr = 0x77541128L - Sections: [, , , , ] \ No newline at end of file + Sections: [, , , , ] + + +.. _sample_network_exploration: + +:class:`Network` - socket exploration +""""""""""""""""""""""""""""""""""""" + +.. literalinclude:: ..\..\samples\network.py + +Output:: + + (cmd λ) python.exe network.py + Working on ipv4 + == Listening == + Some listening connections: [, , ] + Listening ports are : [80, 135, 443, 445, 902, 912, 5357, 49152, 49153, 49154, 49155, 49157, 49159, 8307, 25340, 139, 139] + == Established == + Some established connections: [ 127.0.0.1:49472>, 127.0.0.1:49174>, 127.0.0.1:49173>] + == connection to localhost:80 == + Our connection is [ 127.0.0.1:80>] + Sending YOP + Closing socket + Sending LAIT + Traceback (most recent call last): + File ".\network.py", line 45, in + s.send("LAIT") + socket.error: [Errno 10054] An existing connection was forcibly closed by the remote host \ No newline at end of file diff --git a/doc/source/various.rst b/doc/source/various.rst index 05417ee..a23d87b 100644 --- a/doc/source/various.rst +++ b/doc/source/various.rst @@ -10,4 +10,5 @@ This sections describes them by group of relation. :maxdepth: 2 process.rst - registry.rst \ No newline at end of file + registry.rst + network.rst \ No newline at end of file diff --git a/samples/network.py b/samples/network.py new file mode 100644 index 0000000..5ebc94b --- /dev/null +++ b/samples/network.py @@ -0,0 +1,42 @@ +import sys +import os.path +import socket + +sys.path.append(os.path.abspath(__file__ + "\..\..")) + +import windows + +if not windows.utils.check_is_elevated(): + print("!!! Demo will fail because closing a connection require elevated process !!!") + +print("Working on ipv4") + +conns = windows.system.network.ipv4 +print("== Listening ==") + +print("Some listening connections: {0}".format([c for c in conns if not c.established][:3])) +print("Listening ports are : {0}".format([c.local_port for c in conns if not c.established])) + + +print("== Established ==") + +print("Some established connections: {0}".format([c for c in conns if c.established][:3])) + +TARGET_HOST = "localhost" +TARGET_PORT = 80 + +print("== connection to {0}:{1} ==".format(TARGET_HOST, TARGET_PORT)) + +s = socket.create_connection((TARGET_HOST, TARGET_PORT)) + + +our_connection = [c for c in windows.system.network.ipv4 if c.established and c.remote_port == TARGET_PORT and c.remote_addr == s.getpeername()[0]] + +print("Our connection is {0}".format(our_connection)) + +print("Sending YOP") +s.send("YOP") +print("Closing socket") +our_connection[0].close() +print("Sending LAIT") +s.send("LAIT") \ No newline at end of file diff --git a/windows/network.py b/windows/network.py index 5fe7ed7..0a5b477 100644 --- a/windows/network.py +++ b/windows/network.py @@ -12,26 +12,44 @@ class TCP4Connection(MIB_TCPROW_OWNER_PID): @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("""" closing = MIB_TCPROW() closing.dwState = MIB_TCP_STATE_DELETE_TCB closing.dwLocalAddr = self.dwLocalAddr @@ -66,30 +91,45 @@ class TCP6Connection(MIB_TCP6ROW_OWNER_PID): @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): @@ -157,4 +197,12 @@ class Network(object): 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`] + """ diff --git a/windows/registry.py b/windows/registry.py index b285b77..d92a01b 100644 --- a/windows/registry.py +++ b/windows/registry.py @@ -17,6 +17,7 @@ class ExpectWindowsError(object): KeyValue = collections.namedtuple("KeyValue", ["name", "value", "type"]) class PyHKey(object): + """A windows registry key""" def __init__(self, surkey, name, sam=_winreg.KEY_READ): self.surkey = surkey self.name = name @@ -77,6 +78,7 @@ HKEY_USERS = PyHKey(DummyPHKEY(_winreg.HKEY_USERS, "HKEY_USERS"), "", _winreg.KE class Registry(object): + """The ``Windows`` registry: a read only mapping""" registry_base_keys = { "HKEY_LOCAL_MACHINE" : HKEY_LOCAL_MACHINE, @@ -88,6 +90,14 @@ class Registry(object): } def __getitem__(self, name): + """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: return self.registry_base_keys[name] if "\\" not in name: diff --git a/windows/winproxy.py b/windows/winproxy.py index 8e1b063..d1b6ba8 100644 --- a/windows/winproxy.py +++ b/windows/winproxy.py @@ -28,16 +28,19 @@ class Kernel32Error(WindowsError): class IphlpapiError(Kernel32Error): - def __new__(cls, func_name, code): + def __new__(cls, func_name, code, strerror=None): win_error = ctypes.WinError(code) api_error = super(Kernel32Error, cls).__new__(cls) api_error.api_name = func_name api_error.winerror = win_error.winerror - api_error.strerror = win_error.strerror - api_error.args = (func_name, win_error.winerror, win_error.strerror) + if strerror is not None: + api_error.strerror = strerror + else: + api_error.strerror = win_error.strerror + api_error.args = (func_name, api_error.winerror, api_error.strerror) return api_error - def __init__(self, func_name, code): + def __init__(self, func_name, code, strerror=None): pass @@ -580,7 +583,16 @@ def RegCloseKey(hKey): # ##### Iphlpapi (network list and stuff) ###### # -SetTcpEntry = TransparentIphlpapiProxy('SetTcpEntry') + +def set_tcp_entry_error_check(func_name, result, func, args): + """raise IphlpapiError if result is NOT 0 -- pretty print error 317""" + if result: + if result == 317: + raise IphlpapiError(func_name, result, "".format(func_name)) + raise IphlpapiError(func_name, result) + return args + +SetTcpEntry = TransparentIphlpapiProxy('SetTcpEntry', error_check=set_tcp_entry_error_check) @OptionalExport(IphlpapiProxy('GetExtendedTcpTable'))