Subject: [ruby-ffi] Correct usage of AdjustTokenPrivileges with FFI |
From: Marc Ruehlaender |
Date: 9/12/13 2:53 AM |
To: ruby-ffi@googlegroups.com |
Hi,
I'm new to Ruby and I'm trying to do some GUI-based test automation of a Win32 application using ruby/rspec and ffi.
(ruby 2.0.0p247 (2013-06-27) [i386-mingw32], ffi 1.9.0)
What I am trying to do is click a toolbar button. (More of an exercise than a necessity, since I could of course call the corresponding menu function.)
To get the coordinates for the mouse click I want to send the TB_GETRECT message to the toolbar of my application.
The second parameter of the message takes a pointer which will take a RECT with the bounding rectangle of the button.
Since I need to allocate memory for this pointer to point to in the applications address space, I want to get DEBUG privileges.
And this is where I am getting stumped.
Relevant code patches:
---------------
#file myapp-test.rb
require './ffi_winapi'
module MyApp
#start my app
system 'start "" "<absolute path to my app>"'
#get the handle for the main window
sleep 0.2 while (handle = Win.find_window( nil, '<Window title>') ) <= 0
#get the handle for the tool bar
tool_bar = Win.find_window_ex(handle, 0, "ToolbarWindow32", nil)
#get the process ID for the process in which tool bar is running
ppid = FFI::MemoryPointer.new(:uint32, 1)
tid = Win.get_window_thread_process_id(tool_bar, ppid)
pid = ppid.read_uint32
#open the process for getting a security token
proc = WinKernel.open_process(WinKernel::PROCESS_QUERY_INFORMATION, false, pid)
#get a token handle to adjust privileges
ptoken = FFI::MemoryPointer.new(:long)
bret = WinAuth.open_process_token(proc, WinAuth::TOKEN_QUERY | WinAuth::TOKEN_ADJUST_DEFAULT, ptoken)
token = ptoken.read_long
#intitalize LUID struct
myluid = WinAuth::Luid.new
myluid[:lo] = 0
myluid[:hi] = 0
#get the system LUID for debug privileges
WinAuth.lookup_privilege_value("", WinAuth::SE_DEBUG_NAME, myluid)
#up to this point everything seems to be working fine judging by puts debugging
#prepare the TOKEN_PRIVILEGES struct for adjust token privileges
mytokenprivs = WinAuth::TokenPrivileges.new(FFI::MemoryPointer.new(WinAuth::TokenPrivileges.size_with_privileges(1)))
mytokenprivs[:count] = 1
myluidattr = mytokenprivs.privilege(0)
myluidattr[:luid] = myluid
myluidattr[:attributes] = WinAuth::SE_PRIVILEGE_ENABLED
#code shamelessly adapted from chef code
plen = FFI::MemoryPointer.new(:long).write_long(mytokenprivs.size_with_privileges)
prevtokenprivs = WinAuth::TokenPrivileges.new(FFI::MemoryPointer.new(plen.read_long))
# !!! Failing code !!!
bret = WinAuth.adjust_token_privileges(token, false, mytokenprivs, mytokenprivs.size_with_privileges, prevtokenprivs, plen)
err = WinKernel.get_last_error
puts "Adjust token privileges returned #{bret}; last error: #{err}"
puts "#{prevtokenprivs[:count]} privileges were modified."
#clean up
WinKernel.close_handle(token)
WinKernel.close_handle(proc)
end
#end file myapp_test.rb
---------------
#excerpts from file ffi_winapi.rb
require 'ffi'
module Win
extend FFI::Library
ffi_lib 'user32'
ffi_convention :stdcall
#... (consts, structs and method wrappers for user32)
end
module WinKernel
extend FFI::Library
ffi_lib 'kernel32'
#... (consts, structs and method wrappers for kernel32)
end
module WinAuth
extend FFI::Library
ffi_lib 'advapi32'
TOKEN_QUERY = 0x0008
TOKEN_ADJUST_DEFAULT = 0x0080
# BOOL WINAPI OpenProcessToken(_In_ HANDLE ProcessHandle, _In_ DWORD DesiredAccess, _Out_ PHANDLE TokenHandle);
attach_function :open_process_token, :OpenProcessToken,
[ :long, :int, :pointer ], :bool
class Luid < FFI::Struct
layout :lo, :int,
:hi, :int
end
SE_DEBUG_NAME = "SeDebugPrivilege"
# BOOL WINAPI LookupPrivilegeValue(_In_opt_ LPCTSTR lpSystemName, _In_ LPCTSTR lpName, _Out_ PLUID lpLuid);
attach_function :lookup_privilege_value, :LookupPrivilegeValueA,
[ :string, :string, :pointer], :bool
class LuidAndAttributes < FFI::Struct
layout :luid, Luid,
:attributes, :int
end
SE_PRIVILEGE_ENABLED = 2
#class definition shamelessly adapted from chef code
class TokenPrivileges < FFI::Struct
layout :count, :uint32,
:privileges, LuidAndAttributes
def self.size_with_privileges(num_privileges)
offset_of(:privileges) + LuidAndAttributes.size*num_privileges
end
def size_with_privileges
TokenPrivileges.size_with_privileges(self[:count])
end
def privilege(index)
LuidAndAttributes.new(pointer + offset_of(:privileges) + (index * LuidAndAttributes.size))
end
end
# BOOL WINAPI AdjustTokenPrivileges(_In_ HANDLE TokenHandle, _In_ BOOL DisableAllPrivileges, _In_opt_ PTOKEN_PRIVILEGES NewState, _In_ DWORD BufferLength, _Out_opt_ PTOKEN_PRIVILEGES PreviousState, _Out_opt_ PDWORD ReturnLength);
attach_function :adjust_token_privileges, :AdjustTokenPrivileges,
[ :long, :bool, :pointer, :int, :pointer, :pointer ], :bool
end
#end file ffi_winapi.rb
---------------
When running the ruby code, I get
Adjust token privileges returned false; last error: 0
0 privileges were modified.
Running the equivalent algorithm implemented in visual C++ works fine (i.e. it declares that 1 privilege has been modified, and also the following steps - not shown in the ruby code - lead to a successful read of the RECT.)
Therefore I assume, the error is in my lack of understanding ffi and/or basic ruby concepts.
Any pointers on how to proceed are appreciated.
Cheers,
Marc