From 1fe5b912be10f32599727e1f4ad67f3a2cd3b3c9 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Mon, 28 Aug 2017 17:09:49 +0200 Subject: [PATCH] Add some ALPC samples to the documentation --- docs/source/alpc.rst | 7 ++ docs/source/sample.rst | 120 ++++++++++++++++++++++++++- samples/alpc/advanced_alpc.py | 146 +++++++++++++++++++++++++++++++++ samples/alpc/simple_alpc.py | 50 +++++++++++ samples/alpc_client_serveur.py | 44 ---------- windows/alpc.py | 2 +- 6 files changed, 323 insertions(+), 46 deletions(-) create mode 100644 samples/alpc/advanced_alpc.py create mode 100644 samples/alpc/simple_alpc.py delete mode 100644 samples/alpc_client_serveur.py diff --git a/docs/source/alpc.rst b/docs/source/alpc.rst index 33fded9..83a448a 100644 --- a/docs/source/alpc.rst +++ b/docs/source/alpc.rst @@ -7,6 +7,13 @@ The :mod:`windows.alpc` module regroups the classes that permits to send and rec ALPC messages over an ALPC port and the classes representing these messages. +.. note:: + + See samples: + + * :ref:`sample_alpc` + * :ref:`sample_advanced_alpc` + ALPC Message ------------ diff --git a/docs/source/sample.rst b/docs/source/sample.rst index 831825a..f0221a5 100644 --- a/docs/source/sample.rst +++ b/docs/source/sample.rst @@ -730,4 +730,122 @@ Ouput:: File embdeds 2 certificate(s): * 0) - * 1) \ No newline at end of file + * 1) + + + +``windows.alpc`` +"""""""""""""""" + +.. _sample_alpc: + +simple alpc communication +''''''''''''''''''''''''' + +.. literalinclude:: ..\..\samples\alpc\simple_alpc.py + +Ouput:: + + (cmd λ) python alpc\simple_alpc.py + [SERV] PORT <\RPC Control\PythonForWindowsPORT> CREATED + Client pid = 15044 + [SERV] Message type = 0x300a + [SERV] Received data: <> + [SERV] Connection request + [CLIENT] Connected: + + [SERV] Received message: + [SERV] Message type = 0x3001 + [CLIENT] Response: + BYE + +.. _sample_advanced_alpc: + + +advanced alpc communication +''''''''''''''''''''''''''' + +.. literalinclude:: ..\..\samples\alpc\advanced_alpc.py + + +Output:: + + (cmd λ) python alpc\advanced_alpc.py + server pid = 13000 + [SERV] PORT <\RPC Control\PythonForWindowsPORT_2> CREATED + Client pid = 2100 + [CLIENT] == Connecting to port == + [SERV] == Message received == + * ALPC connection request: + [CLIENT] Connected with response: + + [CLIENT] == Sending a message == + * Sending Message + [SERV] == Message received == + * ALPC request: + * view_is_valid + * security_is_valid + * handle_is_valid + * context_is_valid + * message context attribute: + - CTX.PortContext -> 0x11223344 + - CTX.MessageContext -> None + - CTX.Sequence -> 0x1L + - CTX.MessageId -> 0x0L + - CTX.CallbackId -> 0x0L + * message token attribute: + - TOKEN.TokenId -> 0x1e4ecaccL + - TOKEN.AuthenticationId -> 0x48989L + - TOKEN.ModifiedId -> 0x48995L + [CLIENT] Server response: + [CLIENT] RESP Message Valid ATTRS = [ALPC_MESSAGE_CONTEXT_ATTRIBUTE(0x20000000L)] + + [Client] == Sending a message with a handle == + [SERV] == Message received == + * ALPC request: + * view_is_valid + * security_is_valid + * handle_is_valid + * message handle attribute: + - HANDLE.Flags -> 0x0L + - HANDLE.Handle -> 0x260 + - HANDLE.ObjectType -> 0x1L + - HANDLE.DesiredAccess -> 0x13019fL + - File: ', mode 'r' at 0x02D529C0> + - content: + * context_is_valid + * message context attribute: + - CTX.PortContext -> 0x11223344 + - CTX.MessageContext -> None + - CTX.Sequence -> 0x2L + - CTX.MessageId -> 0x0L + - CTX.CallbackId -> 0x0L + * message token attribute: + - TOKEN.TokenId -> 0x1e4ecaccL + - TOKEN.AuthenticationId -> 0x48989L + - TOKEN.ModifiedId -> 0x48995L + + [Client] == Sending a message with a view == + [SERV] == Message received == + * ALPC request: + * view_is_valid + * message view attribute: + - VIEW.Flags -> 0x0L + - VIEW.SectionHandle -> None + - VIEW.ViewBase -> 0x2770000 + - VIEW.ViewSize -> 0x4000 + * Reading view content: + * security_is_valid + * handle_is_valid + * context_is_valid + * message context attribute: + - CTX.PortContext -> 0x11223344 + - CTX.MessageContext -> None + - CTX.Sequence -> 0x3L + - CTX.MessageId -> 0x0L + - CTX.CallbackId -> 0x0L + * message token attribute: + - TOKEN.TokenId -> 0x1e4ecaccL + - TOKEN.AuthenticationId -> 0x48989L + - TOKEN.ModifiedId -> 0x48995L + BYE diff --git a/samples/alpc/advanced_alpc.py b/samples/alpc/advanced_alpc.py new file mode 100644 index 0000000..ebdcdfb --- /dev/null +++ b/samples/alpc/advanced_alpc.py @@ -0,0 +1,146 @@ +import multiprocessing + +import windows.alpc +from windows.generated_def import LPC_CONNECTION_REQUEST, LPC_REQUEST +import windows.generated_def as gdef + +import ctypes +import tempfile + +PORT_NAME = r"\RPC Control\PythonForWindowsPORT_2" +PORT_CONTEXT = 0x11223344 + + +def full_alpc_server(): + print("server pid = {0}".format(windows.current_process.pid)) + server = windows.alpc.AlpcServer(PORT_NAME) + print("[SERV] PORT <{0}> CREATED".format(PORT_NAME)) + msg = server.recv() + print("[SERV] == Message received ==") + if msg.type & 0xfff == LPC_CONNECTION_REQUEST: + print(" * ALPC connection request: <{0}>".format(msg.data)) + msg.data = "Connection message response" + server.accept_connection(msg, port_context=PORT_CONTEXT) + else: + raise ValueError("Expected connection") + + while True: + msg = server.recv() + print("[SERV] == Message received ==") + # print(" * Data: {0}".format(msg.data)) + # print("[SERV] RECV Message type = {0:#x}".format(msg.type)) + # print("[SERV] RECV Message Valid ATTRS = {0:#x}".format(msg.attributes.ValidAttributes)) + # print("[SERV] RECV Message ATTRS = {0:#x}".format(msg.attributes.AllocatedAttributes)) + if msg.type & 0xfff == LPC_REQUEST: + print(" * ALPC request: <{0}>".format(msg.data)) + print(" * view_is_valid <{0}>".format(msg.view_is_valid)) + if msg.view_is_valid: + print(" * message view attribute:") + windows.utils.print_ctypes_struct(msg.view_attribute, " - VIEW", hexa=True) + view_data = windows.current_process.read_string(msg.view_attribute.ViewBase) + print(" * Reading view content: <{0}>".format(view_data)) + print(" * security_is_valid <{0}>".format(msg.security_is_valid)) + print(" * handle_is_valid <{0}>".format(msg.handle_is_valid)) + if msg.handle_is_valid: + if msg.handle_attribute.Handle: + print(" * message handle attribute:") + windows.utils.print_ctypes_struct(msg.handle_attribute, " - HANDLE", hexa=True) + if msg.handle_attribute.ObjectType == 1: + f = windows.utils.create_file_from_handle(msg.handle_attribute.Handle) + print(" - File: {0}".format(f)) + print(" - content: <{0}>".format(f.read())) + else: + print(" - unknow object type == {0}".format(msg.handle_attribute.ObjectType)) + msg.attributes.ValidAttributes -= gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE + + print(" * context_is_valid <{0}>".format(msg.context_is_valid)) + if msg.context_is_valid: + print(" * message context attribute:") + windows.utils.print_ctypes_struct(msg.context_attribute, " - CTX", hexa=True) + + if msg.attributes.ValidAttributes & gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE: + print(" * message token attribute:") + token_struct = msg.attributes.get_attribute(gdef.ALPC_MESSAGE_TOKEN_ATTRIBUTE) + windows.utils.print_ctypes_struct(token_struct, " - TOKEN", hexa=True) + + # We can reply by to way: + # - Send the same message with modified data + # - Recreate a Message and copy the MessageId + msg.data = "REQUEST '{0}' DONE".format(msg.data) + server.send(msg) + else: + raise ValueError("Unexpected message type") + + +def send_message_with_handle(client): + print "" + print("[Client] == Sending a message with a handle ==") + + # Craft a file with some data + f = tempfile.NamedTemporaryFile() + f.write("Tempfile data <3") + f.seek(0) + + # New message with a Handle + msg = windows.alpc.AlpcMessage() + msg.attributes.ValidAttributes |= gdef.ALPC_MESSAGE_HANDLE_ATTRIBUTE + msg.handle_attribute.Flags = gdef.ALPC_HANDLEFLG_DUPLICATE_SAME_ACCESS + msg.handle_attribute.Handle = windows.utils.get_handle_from_file(f) + msg.handle_attribute.ObjectType = 0 + msg.handle_attribute.DesiredAccess = 0 + msg.data = "some message with a file" + client.send_receive(msg) + +def send_message_with_view(client): + print "" + print("[Client] == Sending a message with a view ==") + + # Create View + section = client.create_port_section(0, 0, 0x4000) + view = client.map_section(section[0], 0x4000) + + # New message with a View + msg = windows.alpc.AlpcMessage(0x2000) + msg.attributes.ValidAttributes |= gdef.ALPC_MESSAGE_VIEW_ATTRIBUTE + msg.view_attribute.Flags = 0 + msg.view_attribute.ViewBase = view.ViewBase + msg.view_attribute.SectionHandle = view.SectionHandle + msg.view_attribute.ViewSize = 0x4000 + msg.data = "some message with a view" + windows.current_process.write_memory(view.ViewBase, "The content of the view :)\x00") + client.send_receive(msg) + +def alpc_client(): + print("Client pid = {0}".format(windows.current_process.pid)) + client = windows.alpc.AlpcClient() + + # You can create a non-connected AlpcClient and send a custom + # 'AlpcMessage' for complexe alpc port connection. + connect_message = windows.alpc.AlpcMessage() + connect_message.data = "Connection request client message" + print("[CLIENT] == Connecting to port ==") + connect_response = client.connect_to_port(PORT_NAME, connect_message) + print("[CLIENT] Connected with response: <{0}>".format(connect_response.data)) + + # AlpcClient send/recv/send_receive methods accept both string or + # AlpcMessage for complexe message. + print"" + print("[CLIENT] == Sending a message ==") + msg = windows.alpc.AlpcMessage() + msg.data = "Complex Message 1" + print(" * Sending Message <{0}>".format(msg.data)) + response = client.send_receive(msg) + print("[CLIENT] Server response: <{0}>".format(response.data)) + print("[CLIENT] RESP Message Valid ATTRS = {0}".format(response.valid_attributes)) + + send_message_with_handle(client) + send_message_with_view(client) + + +if __name__ == "__main__": + proc = multiprocessing.Process(target=full_alpc_server, args=()) + proc.start() + import time; time.sleep(0.5) + alpc_client() + print("BYE") + proc.terminate() \ No newline at end of file diff --git a/samples/alpc/simple_alpc.py b/samples/alpc/simple_alpc.py new file mode 100644 index 0000000..ae778ec --- /dev/null +++ b/samples/alpc/simple_alpc.py @@ -0,0 +1,50 @@ +import multiprocessing + +import windows.alpc +from windows.generated_def import LPC_CONNECTION_REQUEST, LPC_REQUEST + +PORT_NAME = r"\RPC Control\PythonForWindowsPORT" + + +def alpc_server(): + server = windows.alpc.AlpcServer(PORT_NAME) # Create the ALPC Port + print("[SERV] PORT <{0}> CREATED".format(PORT_NAME)) + + msg = server.recv() # Wait for a message + print("[SERV] Message type = {0:#x}".format(msg.type)) + print("[SERV] Received data: <{0}>".format(msg.data)) + assert msg.type & 0xfff == LPC_CONNECTION_REQUEST # Check that message is a connection request + print("[SERV] Connection request") + server.accept_connection(msg) + + msg = server.recv() # Wait for a real message + print "" + print("[SERV] Received message: <{0}>".format(msg.data)) + print("[SERV] Message type = {0:#x}".format(msg.type)) + assert msg.type & 0xfff == LPC_REQUEST + # We can reply by two ways: + # - Send the same message with modified data + # - Recreate a Message and copy the MessageId + msg.data = "REQUEST '{0}' DONE".format(msg.data) + server.send(msg) + + + +def alpc_client(): + print("Client pid = {0}".format(windows.current_process.pid)) + # Creation an 'AlpcClient' with a port name will connect to the port with an empty message + client = windows.alpc.AlpcClient(PORT_NAME) + print("[CLIENT] Connected: {0}".format(client)) + # Send a message / wait for the response + response = client.send_receive("Hello world !") + print("[CLIENT] Response: <{0}>".format(response.data)) + # You can also send message without waiting for a response with 'client.send' + + +if __name__ == "__main__": + proc = multiprocessing.Process(target=alpc_server, args=()) + proc.start() + import time; time.sleep(0.5) + alpc_client() + print("BYE") + proc.terminate() \ No newline at end of file diff --git a/samples/alpc_client_serveur.py b/samples/alpc_client_serveur.py deleted file mode 100644 index f9627b7..0000000 --- a/samples/alpc_client_serveur.py +++ /dev/null @@ -1,44 +0,0 @@ -import multiprocessing - -import windows.alpc -from windows.generated_def import LPC_CONNECTION_REQUEST, LPC_REQUEST - -PORT_NAME = r"\RPC Control\YOLOPORT" - -def alpc_server(): - server = windows.alpc.AlpcServer(PORT_NAME) # NtAlpcCreatePort - print("[SERV] PORT CREATED") - msg = server.recv() # NtAlpcSendWaitReceivePort (send_msg = None) - print("[SERV] Message type = {0:#x}".format(msg.u2.s2.Type)) - print("[SERV] Received data: <{0}>".format(msg.data)) - if msg.type & 0xfff & LPC_CONNECTION_REQUEST: - print("[SERV] Connection request") - msg.data = "WOKAY" - server.accept_connection(msg) # NtAlpcAcceptConnectPort - msg = server.recv() # NtAlpcSendWaitReceivePort (send_msg = None) - print("[SERV] Received message") - print("[SERV] Message type = {0:#x}".format(msg.u2.s2.Type)) - if msg.type & 0xfff & LPC_REQUEST: - print("[SERV] ALPC request: <{0}>".format(msg.data)) - # We can reply by to way: - # - Send the same message with modified data - # - Recreate a Message and copy the MessageId - msg.data = "REQUEST '{0}' DONE".format(msg.data) - server.send(msg) - - -def alpc_client(): - client = windows.alpc.AlpcClient() - connect_response = client.connect_to_port(PORT_NAME, "COUCOU") # NtAlpcConnectPort - print("[CLIENT] Connected: {0}".format(connect_response.data)) - print("[CLIENT] Send Message ") - response = client.send_receive("POUET") # NtAlpcSendWaitReceivePort - print("[CLIENT] Server response: <{0}>".format(response.data)) - - -if __name__ == "__main__": - proc = multiprocessing.Process(target=alpc_server, args=()) - proc.start() - import time; time.sleep(0.5) - alpc_client() - print("BYE") \ No newline at end of file diff --git a/windows/alpc.py b/windows/alpc.py index 62577eb..dd23650 100644 --- a/windows/alpc.py +++ b/windows/alpc.py @@ -404,7 +404,7 @@ class AlpcClient(AlpcTransportBase): else: raise ValueError("Don't know how to send <{0!r}> as connect message".format(connect_message)) - windows.utils.print_ctypes_struct(port_attr, "port_attr_connect", hexa=True) + # windows.utils.print_ctypes_struct(port_attr, "port_attr_connect", hexa=True) receive_attr = MessageAttribute.with_all_attributes() winproxy.NtAlpcConnectPort(handle, port_name_unicode, obj_attr, port_attr, flags, None, send_msg, buffersize, send_msg_attr, receive_attr, timeout) # If send_msg is not None, it contains the ClientId.UniqueProcess : PID of the server :)