A stubborn TypeError in a Windows API function

72 views
Skip to first unread message

Michiel Tegelberg

unread,
Sep 16, 2015, 8:56:03 AM9/16/15
to python-cffi
Hi,

I'm trying to change the privileges for a process in an ABI in-line way. I'm using Python 3.4.3 and CFFI 1.2.1 and I keep getting the following error:

line 100, in <module>
   
ReturnLength)
TypeError: an integer is required


I have tried casting ReturnLength to an integer, dword, ulong, I have even tried to use a number of ctypes types and just giving NULL parameters for the last two parameters of AdjustTokenPrivileges but nothing seems to work. I keep getting the TypeError. Can anyone tell me if I'm doing something wrong, an if I am, what I'm doing wrong?

from cffi import FFI
import inspect
import ctypes
ffi
= FFI()

# Unicode is necessary to use the LPCTSTR type
ffi
.set_unicode(True)
Kernel32 = ffi.dlopen("Kernel32.dll")
advapi32
= ffi.dlopen("Advapi32.dll")

# Define TOKEN_PRIVILEGE, LUID_AND_ATTRIBUTES and LUID structures for later use
ffi
.cdef('''
    int ANYSIZE_ARRAY = 1;

    typedef struct _LUID {
    DWORD LowPart;
    LONG HighPart;
    } LUID, *PLUID;

    typedef struct _LUID_AND_ATTRIBUTES {
    LUID  Luid;
    DWORD Attributes;
    } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;

    typedef struct _TOKEN_PRIVILEGES {
    DWORD               PrivilegeCount;
    LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
    } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

    typedef PVOID HANDLE;
    typedef HANDLE *PHANDLE;
'''
)

# Define LookupPrivilegeValue and look up privilege value for SE_MANAGE_VOLUME_NAME
ffi
.cdef("""
BOOL LookupPrivilegeValueW(LPCTSTR lpSystemName,
                          LPCTSTR lpName,
                          PLUID lpLUID);
"""
)
LUID
= ffi.new("PLUID")
privilegeName
= "SeManageVolumePrivilege"
advapi32
.LookupPrivilegeValueW(ffi.NULL, privilegeName, LUID)
print(privilegeName, "HighPart", LUID[0].HighPart, "LowPart", LUID[0].LowPart)

# Make privilege token to hand to AdjustTokenPrivileges
SE_PRIVILEGE_ENABLED
= 0x00000002  # in WinNT.h
tp
= ffi.new("PTOKEN_PRIVILEGES")
tp
[0].PrivilegeCount = 1
tp
[0].Privileges[0].Luid = LUID[0]
tp
[0].Privileges[0].Attributes = SE_PRIVILEGE_ENABLED

# we can print the whole struct \o/
print("tp PrivilegeCount", tp[0].PrivilegeCount)
print("tp LUID_AND_ATTRIBUTES Attributes", tp[0].Privileges[0].Attributes)
print("tp LUID_AND_ATTRIBUTES LUID HighPart", tp[0].Privileges[0].Luid.HighPart, "LowPart", tp[0].Privileges[0].Luid.LowPart)


# Retreive pseudo handle for current process
ffi
.cdef("""
HANDLE GetCurrentProcess(void);
"""
)
pHandle
= Kernel32.GetCurrentProcess()
print(pHandle)

# Open Privilege Token for current process
ffi
.cdef("""
BOOL OpenProcessToken(HANDLE ProcessHandle,
                      DWORD DesiredAccess,
                      PHANDLE TokenHandle);
"""
)
TOKEN_ADJUST_PRIVILEGES
= 0x20
TOKEN_QUERY
= 0x8
hToken
= ffi.new("PHANDLE")
advapi32
.OpenProcessToken(pHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, hToken)
print(ffi.getwinerror())

# Maken PTOKEN_PRIVILEGES to hold the old privileges
oldTP
= ffi.new("PTOKEN_PRIVILEGES")
tp_buffer
= ffi.buffer(oldTP)
ReturnLength = ffi.new("PDWORD")
print(ReturnLength)
print("ReturnLength: ", ReturnLength)
print("tp_buffer: ", tp_buffer)

# Adjust the process' privileges
ffi
.cdef("""
BOOL AdjustTokenPrivileges(HANDLE TokenHandle,
                           BOOL DisableAllPrivileges,
                           PTOKEN_PRIVILEGES NewState,
                           DWORD BufferLength,
                           PTOKEN_PRIVILEGES PreviousState,
                           PDWORD ReturnLength);
"""
)

advapi32
.AdjustTokenPrivileges(hToken,
                               
False,
                               tp
,
                               tp_buffer
,
                               oldTP
,
                               
ReturnLength)


Armin Rigo

unread,
Sep 16, 2015, 6:05:44 PM9/16/15
to pytho...@googlegroups.com
Hi Michiel,

On Wed, Sep 16, 2015 at 2:56 PM, Michiel Tegelberg
<michielt...@gmail.com> wrote:
> line 100, in <module>
> ReturnLength)
> TypeError: an integer is required

The error is confusing because it shows only the last line of the
call, whereas it could actually come from any argument, not
necessarily "ReturnLength". In this case, I see a mismatch both in
the "HANDLE TokenHandle" argument (to which you pass a PHANDLE instead
of a HANDLE), and in the "DWORD BufferLength" argument (to which you
pass a ffi.buffer() instead of an integer).

Maybe cffi could make this clearer by inserting "argument 1: " in
front of the error message.


A bientôt,

Armin.

Michiel Tegelberg

unread,
Sep 17, 2015, 3:37:05 AM9/17/15
to pytho...@googlegroups.com
Hey Armin,

Thanks for your help, I changed ffi.buffer to ffi.sizeof and it worked perfectly. Also having cffi tell me what argument was faulty would've been really helpful as it did not even come to mind the faulty parameter could be anything other than ReturnLength. I guess the only thing left to say is that assumption is the mother of all screw-ups.


Thanks again,

Michiel


--
-- python-cffi: To unsubscribe from this group, send email to python-cffi...@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/python-cffi?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "python-cffi" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-cffi/R89P4CrdWzc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-cffi...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Armin Rigo

unread,
Sep 17, 2015, 2:30:53 PM9/17/15
to pytho...@googlegroups.com
Hi Michiel,

On Thu, Sep 17, 2015 at 9:37 AM, Michiel Tegelberg
<michielt...@gmail.com> wrote:
> Thanks for your help, I changed ffi.buffer to ffi.sizeof and it worked
> perfectly. Also having cffi tell me what argument was faulty would've been
> really helpful as it did not even come to mind the faulty parameter could be
> anything other than ReturnLength. I guess the only thing left to say is that
> assumption is the mother of all screw-ups.

That's actually a property of Python: in a call, the arguments are
collected one by one and then send all together to the called
function. If there is a wrong argument, the called function
complains. At this point there is no way the interpreter knows that
the exception is about a specific argument (as opposed to anything
else that can go wrong with the execution of that function), so it
cannot show the correct line. Example: try to run:

setattr(43.5, 'cant_set_attribute',
'new value')

It complains about the float in the first line, but the traceback
shows the second line.


A bientôt,

Armin.
Reply all
Reply to author
Forward
0 new messages