hakril / PythonForWindows

A codebase aimed to make interaction with Windows and native execution easier
BSD 3-Clause "New" or "Revised" License
569 stars 114 forks source link

Fix TypeError bug #38

Closed g3rzi closed 1 year ago

g3rzi commented 2 years ago

Hi,

Great project. I used a simple RPC example to run the server.
I had a bug when I used James Forshaw function Get-RpcAlpcServer, from his RPC tool (NtObjectManager).

The application throw an error:

  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python310\lib\site-packages\windows\alpc.py", line 164, in write_data
    self.raw_buffer[self.header_size: self.header_size + len(data)] = data
TypeError: one character bytes, bytearray or integer expected

I fixed it by returning the data as bytes.

hakril commented 2 years ago

Hi, Thank you for the PR. In order to better identify the cause, implication of the bug and potential similar bug in the code I would like to write an associated test. Do you have any information about the code that triggered the bug ? Is it in the code of a python RPC server ? What were the types involved ?

Thank you,

g3rzi commented 2 years ago

Yes, I noticed it happens after the initializing of the client:

client = windows.alpc.AlpcClient(PORT_NAME)

It then go to the __init__ function of AlpcClient and calls (link):

x = self.connect_to_port(port_name, "")

The connect_message is being set as ".
When there is an assignment to send_msg.data (link), it throws the error. This is the assignment:

send_msg.data = connect_message

After stepping into the above assignment it goes to write_data function and you can see the value of data which is ":
image

Notice that I am using latest Python version 3.10 and this is the code to reproduce it (tested on two systems):

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()