How To Use The win32api with python3

cpu0x00
System Weakness
Published in
8 min readApr 11, 2023

--

I wanted to write a simple guide on how i interface with the windows api using python-ctypes im by no means an expert i just wanted to share the way i do it because i feel like there isn’t alot of explanations on the internet when it comes to interfacing with the windows api, thats been said so lets get right into it

you can do everything with python even C++ you can do it with python

here is a couple things you need to understand before you start:

1- ctypes calls functions out of an actual C++ code so you need to keep in mind the datatype difference when calling C++ functions means that you cant pass a python datatype directly to C++ functions

2- as the first point python doesn’t know how to handle C++ returns and function arguments out of the box so you need to tell it how to do this

3- the microsoft documentation is your best friend

knowing this lets take a look at a simple but juicy example, i will be calling the function GlobalMemoryStatusEx() from kernel32 which gets how much memory exists on a machine

lets open the documentation of this function to understand what does it takes as a variable and what its return value

from the doc:

from the above image we can see 2 things:

  • the function’s return value is a BOOL
  • the function takes an lpBuffer argument of an LPMEMORYSTATUSEX datatype

in any function’s documentation when it comes to arguments the one in the middle is the Datatype of the argument which must be matched in the python code

an LPMEMORYSTATUSEX means a POINTER to memorystatusex but what is memorystatusex? scrolling down a bit more to see:

from the microsoft docs:

the Parameters section

MEMORYSTATUSEX is a C++ Structure GlobalMemoryStatusEx() uses this structure to populate its fields with the required information about the memory based on every field in the structure and since python doesn’t use structures we gonna need to define this structure from scratch using ctypes to match the datatype of the function , to do this we have to look at the documentation of this structure here

MEMORYSTATUSEX Syntax

looks intimidating a bit but its no big deal, this structure contains 9 fields 7 of them is a DWORDLONG and 2 is DWORD with this in mind, lets get our hands dirty and open a get_memory.py file

first import the necessary libraries

from ctypes import wintypes
from ctypes import windll
import ctypes

wintypes contains the vast majority of datatypes that can be used with the win32api and python-ctypes

windll is basically the windows.h file which is used call other header files like kernel32

ctypes used for extra datatype definition

our imports are ready lets start by defining the structure with its required fields, now as said before python doesn’t use structures so how to define a structure?

well, python classes can inherit nearly any datatype so we can define a class that inherits the type of a C structure like this

class MEMORYSTATUSEX(ctypes.Structure):

now we have a class that inherits from a C structure which makes it A structure, it time to define this structure’s fields, to do this we are going to use the (_fields_) attribute which is a ctypes.Structure based attribute and remember this class is a ctypes.Structure, this is how its done:

class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
('VAR_NAME', DATATYPE),
]

(_fields_) attribute is list every field of this list is tuple and every tuple takes the variable name and its corresponding datatype from the documentation

now lets put this to work and actually define the fields with its datatypes

the first field is (dwLength) which is a DWORD so thats how it will be:

(‘dwLength’, wintypes.DWORD), same as the second one which is also a DWORD

now the other variables are DWORDLONG type and this type is not defined in wintypes

to check for a type whether its defined or not in wintypes open a python interpreter import wintypes and try to call the type like this:

checking for DWORDLONG

so what now?

here comes the ctypes datatypes, if we checked the actual type of wintypes.DWORD we will see this

its actually a (ctypes.c_ulong) type so if the DWORD is c_ulong then the DWORDLONG is a c_ulonglong so the other variables will just be a ctypes.c_ulonglong, applying this gives us the final fields of the structure

class MEMORYSTATUSEX(ctypes.Structure): 
_fields_ = [
('dwlength', wintypes.DWORD),
('dwMemoryLoad', wintypes.DWORD),
('ullTotalPhys', ctypes.c_ulonglong),
('ullAvailPhys', ctypes.c_ulonglong),
('ullTotalPageFile', ctypes.c_ulonglong),
('ullAvailPageFile', ctypes.c_ulonglong),
('ullTotalVirtual', ctypes.c_ulonglong),
('ullAvailVirtual', ctypes.c_ulonglong),
('ullAvailExtendedVirtual', ctypes.c_ulonglong),
]

now from the microsoft docs of the structure again this is said:

dwLength

The size of the structure, in bytes. You must set this member before calling
GlobalMemoryStatusEx.

and they gave us an example of this in GlobalMemoryStatusEx documentation:

setting the structure size to itself

in the provided example they set the structure size to itself immediately after its initialized now the way we do this in python is a bit different, we are going to use the (__init__) function or dunder method call it whatever you like it

this function is going to run immediately once the class/structure is initialized, so lets set the dwLength that we defined above to the size of the structure itself

class MEMORYSTATUSEX(ctypes.Structure): 
_fields_ = [
('dwlength', wintypes.DWORD),
('dwMemoryLoad', wintypes.DWORD),
('ullTotalPhys', ctypes.c_ulonglong),
('ullAvailPhys', ctypes.c_ulonglong),
('ullTotalPageFile', ctypes.c_ulonglong),
('ullAvailPageFile', ctypes.c_ulonglong),
('ullTotalVirtual', ctypes.c_ulonglong),
('ullAvailVirtual', ctypes.c_ulonglong),
('ullAvailExtendedVirtual', ctypes.c_ulonglong),
]

def __init__(self):
self.dwlength = ctypes.sizeof(self)
super(MEMORYSTATUSEX, self).__init__()
  • self.dwlength is dwlength defined in the _fields_ attribute
  • ctypes.sizeof() is the C function used to get the size of the structure
  • super() will immediately call the __init__() function

from w3schools:

The super() function is used to give access to methods and properties of a parent or sibling class.

The super() function returns an object that represents the parent class.

ctypes.Structure is the parent and MEMORYSTATUSEX is the child

now the Structure is Done lets look at the function itself

now as said above datatypes are a BIG deal when using ctypes so its important to get the datatypes of the function right otherwise its not gonna work, to do this we need to define 2 things:

  • the function’s argument(s) type
  • the function’s return type

from the doc of the function, it takes one argument of the type of a LPMEMORYSTATUSEX which is a pointer to the MEMORYSTATUSEX structure

and it returns a Boolian

variable and return types

so lets call kernel32 from the imported windll and call the function from kernel32 and redefine its args and return types

kernel32 = windll.kernel32

GlobalMemoryStatusEx = kernel32.GlobalMemoryStatusEx
GlobalMemoryStatusEx.argtype = [ctypes.POINTER(MEMORYSTATUSEX)]
GlobalMemoryStatusEx.restype = wintypes.BOOL

points to mention:

  • setting the (GlobalMemoryStatusEx) variable to the function from kernel32
  • argtype is list that contains argument types, in this case it took one argument which is a C pointer to MEMORYSTATUSEX that we defined before
  • restype is the return type and is not a list since it is only one type all the time

now we successfully told python that this function’s argument is that datatype

lets now call the function

this is how the function is called from the microsoft documentation

keep in mind that not all the functions have examples of calling them in the docs

  • so they defined a statex variable that holds the structure
  • they got the size of the structure (we already did this)
  • they called the function with a pointer to the variable that holds the structure

now lets replicate:

statex = MEMORYSTATUSEX()
GlobalMemoryStatusEx(ctypes.byref(statex))
  • we set the statex variable to the created structure and automatically the dwlength will be set for us by the __init__() function
  • and we call the function with the argument of ctypes.byref(statex)

byref means by reference it references an object “points” to it, so its the pointer to the statex variable

don’t confuse it with ctypes.POINTER

  • ctypes.POINTER() is used to create a pointer to a C data type. When you use ctypes.POINTER, you are creating a new object that points to the memory location of the original object
  • ctypes.byref, on the other hand, is used to create a reference to an existing C data type. When you use ctypes.byref, you are creating a reference to the original object, which means that any changes you make to the reference will also affect the original object.

new we called our function it should populate the fields of the created structure accordingly so lets access one of those fields and print its value,

im gonna use (ullTotalPhys) field since that is the field that contains the total physical memory

to do this we can easily print it out like this

print(statex.ullTotalPhys)

but this gonna give a wierd number which isn’t your total memry, well it is but in bytes so you need to divide by (1024) 3 times to get the accurate value

and if you want just a number and don’t care about decimal places round up this division with the round() function

so here is everything put together:

from ctypes import wintypes
from ctypes import windll
import ctypes



class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
('dwlength', wintypes.DWORD),
('dwMemoryLoad', wintypes.DWORD),
('ullTotalPhys', ctypes.c_ulonglong),
('ullAvailPhys', ctypes.c_ulonglong),
('ullTotalPageFile', ctypes.c_ulonglong),
('ullAvailPageFile', ctypes.c_ulonglong),
('ullTotalVirtual', ctypes.c_ulonglong),
('ullAvailVirtual', ctypes.c_ulonglong),
('ullAvailExtendedVirtual', ctypes.c_ulonglong),

]

def __init__(self):
self.dwlength = ctypes.sizeof(self)
super(MEMORYSTATUSEX, self).__init__()


kernel32 = windll.kernel32

GlobalMemoryStatusEx = kernel32.GlobalMemoryStatusEx
GlobalMemoryStatusEx.argtype = [ctypes.POINTER(MEMORYSTATUSEX)]
GlobalMemoryStatusEx.restype = wintypes.BOOL


statex = MEMORYSTATUSEX()

GlobalMemoryStatusEx(ctypes.byref(statex))

print(f'[*] physical memory: {round(statex.ullTotalPhys/1024/1024/1024)}GB')

here is the result:

running the code

all this may seem intimidating at a glance but it really isn’t once you get the basic core concepts the rest is just looking at microsoft docs :\ ahh.. easy the rest is easy

if i made anything a little vague please don’t hesitate to reach out

twitter

discord: cpu#5416

github

--

--