Need help with PythonForWindows?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

hakril
370 Stars 93 Forks BSD 3-Clause "New" or "Revised" License 629 Commits 0 Opened issues

Description

A codebase aimed to make interaction with Windows and native execution easier

Services available

!
?

Need anything else?

Contributors list

# 125,852
Python
rpc
Windows
wmi
505 commits
# 20,544
hexrays
C#
Windows
WPF
7 commits
# 144,010
Windows
wmi
D
Shell
1 commit
# 3,156
sass-fr...
splash
Socket....
pipelin...
1 commit
# 255,245
C++
Shell
C
xor
1 commit

PythonForWindows

Join the chat at https://gitter.im/PythonForWindows/general

PythonForWindows (PFW) is a base of code aimed to make interaction with

Windows
(on X86/X64) easier (for both 32 and 64 bits Python). Its goal is to offer abstractions around some of the OS features in a (I hope) pythonic way. It also tries to make the barrier between python and native execution thinner in both ways. There is no external dependencies but it relies heavily on the
ctypes
module.

Some of this code is clean (IMHO) and some parts are just a wreck that works for now. Let's say that the codebase evolves with my needs and my curiosity.

Complete online documentation is available here You can find some examples of code in the samples directory or online.

PythonForWindows is principally known for its ALPC-RPC Client (see samples).

If you have any issue, question or suggestion do not hesitate to join the Gitter channel. I am always glad to have feedbacks from people using this project.

Installation

PythonForWindows is available on Pypi an this can be installed with

python -m pip install PythonForWindows

You can also install PythonForWindows by cloning it and using the

setup.py
script:

python setup.py install

Python3

python3 support is still in beta. All the tests pass on master, but I did not test it heavily on real case. Do not hesitate report bugs and issues.

Overview

Processes / Threads

PythonForWindows offers objects around processes and allows you to:

  • Retrieve basic process informations (pid, name, ppid, bitness, ...)
  • Perform basic interprocess operation (allocation, create thread, read/write memory)
  • Explore the PEB (Process Environment Block)
  • Execute
    native
    and
    Python
    code in the context of a process.

I try my best to make those features available for every cross-bitness processes (

32  64
in both ways). This involves relying on non-documented
Windows
functions/behaviours and also injecting code in the 64bits world of a
Syswow64
process. All those operations are also available for the
current_process
.

You can also make some operation on threads (suspend/resume/wait/get(or set) context/ kill)

>>> import windows
>>> windows.current_process.bitness
32
>>> windows.current_process.token.integrity
SECURITY_MANDATORY_MEDIUM_RID(0x2000)
>>> proc = [p for p in windows.system.processes if p.name == "notepad.exe"][0]
>>> proc

>>> proc.bitness
64
>>> proc.peb.modules[:3]
[, , ]
>>> k32 = proc.peb.modules[2]
>>> hex(k32.pe.exports["CreateFileW"])
'0x7ffee6761550L'
>>> proc.threads[0]

>>> hex(proc.threads[0].context.Rip)
'0x7ffee68b54b0L'
>>> proc.execute_python("import os")
True
>>> proc.execute_python("exit(os.getpid() + 1)")
# execute_python raise if process died
Traceback (most recent call last):
...
WindowsError:  died during execution of python command
>>> calc

>>> calc.exit_code
16521L

System information

Information about the Windows computer running the script are available through the

windows.system
object.
>>> windows.system

>>> windows.system.bitness
64
>>> windows.system.computer_name
'DESKTOP-VKUGISR'
>>> windows.system.product_type
VER_NT_WORKSTATION(0x1)
>>> windows.system.version
(10, 0)
>>> windows.system.version_name
'Windows 10'
>>> windows.system.build_number
'10.0.15063.608'

windows.system also contains dynamic lists about processes / threads / handles / ...

>>> windows.system.handles[-2:] [ in process pid=14360>, in process pid=14360>] >>> windows.system.processes[:2] [, ] >>> windows.system.logicaldrives[0] >>> windows.system.services[23]

</windows.winobject.system.system>

IAT Hook

This codebase is born from my need to have IAT hooks implemented in Python. So the features is present (See online documentation about IAT hooks).

Winproxy

A wrapper around some Windows functions. Arguments name and order are the same, but some have default values and the functions raise exception on call error (I don't like

if
around all my call).
>>> import windows
>>> help(windows.winproxy.VirtualAlloc)
# Help on function VirtualAlloc in module windows.winproxy:
# VirtualAlloc(lpAddress=0, dwSize=NeededParameter, flAllocationType=MEM_COMMIT(0x1000L), flProtect=PAGE_EXECUTE_READWRITE(0x40L))
#     Errcheck:
#     raise WinproxyError if result is 0

Positional arguments

>>> windows.winproxy.VirtualAlloc(0, 0x1000) 34537472

Keyword arguments

>>> windows.winproxy.VirtualAlloc(dwSize=0x1000) 34603008

NeededParameter must be provided

>>> windows.winproxy.VirtualAlloc() """ Traceback (most recent call last): File "", line 1, in File "windows\winproxy.py", line 264, in VirtualAlloc return VirtualAlloc.ctypes_function(lpAddress, dwSize, flAllocationType, flProtect) File "windows\winproxy.py", line 130, in perform_call raise TypeError("{0}: Missing Mandatory parameter ".format(self.func_name, param_name)) TypeError: VirtualAlloc: Missing Mandatory parameter """

Error raises exception

>>> windows.winproxy.VirtualAlloc(dwSize=0xffffffff) """ Traceback (most recent call last): File "", line 1, in File "windows\winproxy.py", line 264, in VirtualAlloc return VirtualAlloc.ctypes_function(lpAddress, dwSize, flAllocationType, flProtect) File "windows\winproxy.py", line 133, in perform_call return self._cprototyped(*args) File "windows\winproxy.py", line 59, in kernel32_error_check raise WinproxyError(func_name) windows.winproxy.error.WinproxyError: VirtualAlloc: [Error 87] The parameter is incorrect. """

Native execution

To make the barrier between

native
and
Python
code thinner, PythonForWindows allows you to create native function callable from Python (thanks to
ctypes
) and also embed a simple x86/x64 assembler.
>>> import windows.native_exec.simple_x86 as x86
>>> code = x86.MultipleInstr()
>>> code += x86.Mov("EAX", 41)
>>> code += x86.Inc("EAX")
>>> code += x86.Ret()
>>> code.get_code()
'\xc7\xc0)\x00\x00\[email protected]\xc3'
# Create a function that takes no parameters and return an uint
>>> f = windows.native_exec.create_function(code.get_code(), [ctypes.c_uint])
>>> f()
42L
# Assemblers can also be used in a more standard way
>>> x86.assemble("cmp edi, 0; jnz :end; mov eax, 1; label :end; ret")
'\x81\xff\x00\x00\x00\x00u\x06\xc7\xc0\x01\x00\x00\x00\xc3'

Token / Security Descriptor

Objects easing access to some information about

Token
and
SecurityDescriptor
are also available.
>>> import windows.security
>>> import windows.generated_def as gdef
>>> tok = windows.current_process.token
>>> tok

>>> tok.username
u'hakril'
>>> tok.type
tagTOKEN_TYPE.TokenPrimary(0x1)
>>> tok.integrity
SECURITY_MANDATORY_MEDIUM_RID(0x2000)
>>> tok.duplicate(type=gdef.TokenImpersonation, impersonation_level=gdef.SecurityIdentification)


Security Descriptor

>>> sd = windows.security.SecurityDescriptor.from_filename("c:\windows\system32\kernel32.dll") >>> sd >>> windows.utils.lookup_sid(sd.owner) (u'NT SERVICE', u'TrustedInstaller') >>> sd.dacl >>> list(sd.dacl) [, , , , , ] >>> sd.dacl[1].sid

Wintrust

To easily script some signature check script, PythonForWindows implements some wrapper functions around

wintrust.dll
>>> import windows.wintrust
>>> windows.wintrust.is_signed(r"C:\Windows\system32\ntdll.dll")
True
>>> windows.wintrust.is_signed(r"C:\Windows\system32\python27.dll")
False
>>> windows.wintrust.full_signature_information(r"C:\Windows\system32\ntdll.dll")
SignatureData(signed=True,
    catalog=u'C:\\Windows\\system32\\CatRoot\\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\\Package_35_for_KB3128650~31bf3856ad364e35~amd64~~6.3.1.2.cat',
    catalogsigned=True, additionalinfo=0L)
>>> windows.wintrust.full_signature_information(r"C:\Windows\system32\python27.dll")
SignatureData(signed=False, catalog=None, catalogsigned=False, additionalinfo=TRUST_E_NOSIGNATURE(0x800b0100))

WMI

To extract/play with even more information about the system, PythonForWindows is able to perform WMI request.

>>> import windows
>>> windows.system.wmi.select
>
>>> windows.system.wmi.select("Win32_Process")[:3]
[, , ]# Get WMI data for current process
>>> windows.system.wmi.select("Win32_Process")[42]["Name"]
u'svchost.exe'
>>> wmi_cp = [p for p in windows.system.wmi.select("Win32_Process") if int(p["Handle"]) == windows.current_process.pid][0]
>>> wmi_cp["CommandLine"], wmi_cp["HandleCount"]
(u'"C:\\Python27\\python.exe"', 227)

Registry

The project also contains some wrapping classes around

_winreg
for simpler use.
>>> import windows
>>> from windows.generated_def import KEY_WRITE, KEY_READ, REG_QWORD
>>> registry = windows.system.registry
>>> cuuser_software = registry(r'HKEY_CURRENT_USER\Software')
>>> cuuser_software

>>> cuuser_software.sam
KEY_READ(0x20019)
# Explore subkeys
>>> cuuser_software.subkeys[:3]
[, , ]
>>> tstkey = registry('HKEY_CURRENT_USER\TestKey',  KEY_WRITE | KEY_READ)
# Get / Set individual value
>>> tstkey["VALUE"] = 'a_value_for_my_key'
>>> tstkey["VALUE"]
KeyValue(name='VALUE', value=u'a_value_for_my_key', type=1)
>>> tstkey["MYQWORD"] = (123456789987654321, REG_QWORD)  # Default is REG_DWORD for int/long
>>> tstkey["MYQWORD"]
KeyValue(name='MYQWORD', value=123456789987654321L, type=11)
# Explore Values
>>> tstkey.values
[KeyValue(name='MYQWORD', value=123456789987654321L, type=11), KeyValue(name='VALUE', value=u'a_value_for_my_key', type=1)]

Object manager

PythonForWindows uses the native Windows NT API to display some information about the object in the Object Manager's name space. Just like the well-known tools

winobj.exe
>>> windows.system.object_manager.root

# The objects of type "Directory" can be acceded just like a dict
>>> list(windows.system.object_manager.root)[:3]
[u'PendingRenameMutex', u'ObjectTypes', u'storqosfltport']
# Find an object by its path
>>> windows.system.object_manager["KnownDLLs\\kernel32.dll"]

>>> k32 = windows.system.object_manager["KnownDLLs\\kernel32.dll"]
>>> k32.name, k32.fullname, k32.type
('kernel32.dll', '\\KnownDLLs\\kernel32.dll', u'Section')
# Follow SymbolicLink object
>>> windows.system.object_manager["\\KnownDLLs\\KnownDLLPath"]

>>> windows.system.object_manager["\\KnownDLLs\\KnownDLLPath"].target
u'C:\\WINDOWS\\System32'

Scheduled Task

The

windows.system.task_scheduler
object allows to query and create scheduled task.

This part is still in developpement and the API may evolve

>>> windows.system.task_scheduler

>>> windows.system.task_scheduler.root

>>> task = windows.system.task_scheduler.root.tasks[2]
>>> task

>>> task.name
u'DemoTask'
# Explore task actions
>>> task.definition.actions[1]

>>> task.definition.actions[1].path
u'c:\\windows\\python\\python.exe'
>>> task.definition.actions[1].arguments
u'yolo.py --test'

Event logs

The

windows.system.event_log
object allows to query event logs.

This part is still in developpement and the API may evolve

>>> windows.system.event_log

# Find a channel by its name
>>> chan = windows.system.event_log["Microsoft-Windows-Windows Firewall With Advanced Security/Firewall"]
>>> chan

# Open .evtx files
>>> windows.system.event_log["test.evtx"]

# Query a channel for all events
>>> chan.query().all()[:2]
[, ]
# Query a channel for some ids
>>> chan.query(ids=2004).all()[:2]
[, ]
# Query a channel via XPATH
>>> evt = chan.query("Event/EventData[Data='Netflix']").all()[0]
# Explore event information
>>> evt

>>> evt.data
{u'ModifyingUser': 69828304, u'RuleName': u'Netflix', u'ModifyingApplication': ...}

ALPC-RPC

ALPC

Classes around Advanced Local Procedure Call (ALPC) syscalls allows to simply write client and server able to send ALPC messages.

>>> import windows.alpc
# Test server juste reply to each message with "REQUEST '{msg_data}' RECEIVED"
>>> client = windows.alpc.AlpcClient(r"\RPC Control\PythonForWindowsTESTPORT")
>>> response = client.send_receive("Hello world !")
>>> response

>>> response.data
"REQUEST 'Hello world !' RECEIVED"

Full client/server code for this example is available is the ALPC samples along with a more complex example.

RPC

An RPC-Client based using ALPC communication is also integred

# Server (port ALPC '\RPC Control\HelloRpc') offers:
# Interface '41414141-4242-4343-4444-45464748494a' version 1.0
#   Method 1 -> int Add(int a, int b) -> return a + b
# This Test server is a real RPC Server using rpcrt4.dll and compiled with VS2015.

>>> import windows.rpc >>> from windows.rpc import ndr >>> client = windows.rpc.RPCClient(r"\RPC Control\HelloRpc") >>> client <windows.rpc.client.rpcclient object at> >>> iid = client.bind("41414141-4242-4343-4444-45464748494a") >>> ndr_params = ndr.make_parameters([ndr.NdrLong] * 2)

NDR pack + Make RPC call to method 1.

>>> resp = client.call(iid, 1, ndr_params.pack([41414141, 1010101]))

Unpack the NDR response

>>> result = ndr.NdrLong.unpack(ndr.NdrStream(resp)) >>> result 42424242 </windows.rpc.client.rpcclient>

A sample with the User Account Control (UAC) and one with

lsasrv.dll
are available in the RPC samples.

Debugger

PythonForWindows provides a standard debugger to debug other processes.

import windows
import windows.debug
import windows.test
import windows.native_exec.simple_x86 as x86
import windows.generated_def as gdef

from windows.test import pop_proc_32

class MyDebugger(windows.debug.Debugger): def on_exception(self, exception): code = exception.ExceptionRecord.ExceptionCode addr = exception.ExceptionRecord.ExceptionAddress print("Got exception {0} at 0x{1:x}".format(code, addr)) if code == gdef.EXCEPTION_ACCESS_VIOLATION: print("Access Violation: kill target process") self.current_process.exit()

calc = windows.test.pop_proc_32(dwCreationFlags=gdef.DEBUG_PROCESS) d = MyDebugger(calc) calc.execute(x86.assemble("int3; mov [0x42424242], EAX; ret")) d.loop()

Ouput

Got exception EXCEPTION_BREAKPOINT(0x80000003) at 0x77e13c7d Got exception EXCEPTION_BREAKPOINT(0x80000003) at 0x230000 Got exception EXCEPTION_ACCESS_VIOLATION(0xc0000005) at 0x230001 Access Violation: kill target process

The debugger handles

  • Standard breakpoint
    int3
  • Hardware Execution breakpoint
    DrX
  • Memory breakpoint
    virtual protect

LocalDebugger

You can also debug your own process (or debug a process by injection) via the LocalDebugger.

The LocalDebugger is an abstraction around Vectored Exception Handler (VEH)

import windows
from windows.generated_def.winstructs import *
import windows.native_exec.simple_x86 as x86

class SingleSteppingDebugger(windows.debug.LocalDebugger): SINGLE_STEP_COUNT = 4 def on_exception(self, exc): code = self.get_exception_code() context = self.get_exception_context() print("EXCEPTION !!!! Got a {0} at 0x{1:x}".format(code, context.pc)) self.SINGLE_STEP_COUNT -= 1 if self.SINGLE_STEP_COUNT: return self.single_step() return EXCEPTION_CONTINUE_EXECUTION

class RewriteBreakpoint(windows.debug.HXBreakpoint): def trigger(self, dbg, exc): context = dbg.get_exception_context() print("GOT AN HXBP at 0x{0:x}".format(context.pc)) # Rewrite the infinite loop with 2 nop windows.current_process.write_memory(self.addr, "\x90\x90") # Ask for a single stepping return dbg.single_step()

d = SingleSteppingDebugger()

Infinite loop + nop + ret

code = x86.assemble("label :begin; jmp :begin; nop; ret") func = windows.native_exec.create_function(code, [PVOID]) print("Code addr = 0x{0:x}".format(func.code_addr))

Create a thread that will infinite loop

t = windows.current_process.create_thread(func.code_addr, 0)

Add a breakpoint on the infinite loop

d.add_bp(RewriteBreakpoint(func.code_addr)) t.wait() print("Done!")

Output

Code addr = 0x6a0002 GOT AN HXBP at 0x6a0002 EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004) at 0x6a0003 EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004) at 0x6a0004 EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004) at 0x6a0005 EXCEPTION !!!! Got a EXCEPTION_SINGLE_STEP(0x80000004) at 0x770c7c04 Done!

The local debugger handles

  • Standard breakpoint
    int3
  • Hardware Execution breakpoint
    DrX

Other stuff (see doc / samples)

  • Network
  • COM

Acknowledgments

  • clmntb for his initial work on
    windows.security

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.