Re: [python-win32] taskscheduler: how to tick setting "Run task as soon as possible after scheduled start is missed"

2023-01-09 Thread Eryk Sun
On 1/5/23, Joel Moberg  wrote:
> Maybe I can adjust the flag in the Trigger object but I have no idea what
> flags are available.

The following loosely adapts Microsoft's time trigger task example,
with commented links to get further information. The property that
directly addresses your question is
taskDefinition.Settings.StartWhenAvailable.

---

import win32com.client
from pywintypes import com_error
from datetime import datetime, timedelta

# Connect a TaskService instance.
# https://learn.microsoft.com/windows/win32/taskschd/taskservice

service = win32com.client.Dispatch('Schedule.Service')
service.Connect()

# Create a TaskDefinition.
# https://learn.microsoft.com/windows/win32/taskschd/taskdefinition

taskDefinition = service.NewTask(0)

# Set the task description and author name.
# https://learn.microsoft.com/windows/win32/taskschd/registrationinfo

taskDefinition.RegistrationInfo.Description = (
"Start notepad at a certain time")
taskDefinition.RegistrationInfo.Author = "Author Name"

# Enable the task to start after the scheduled time.
# https://learn.microsoft.com/windows/win32/taskschd/tasksettings

taskDefinition.Settings.Enabled = True
taskDefinition.Settings.StartWhenAvailable = True

# Add a TimeTrigger.
# https://learn.microsoft.com/windows/win32/taskschd/triggercollection
# https://learn.microsoft.com/windows/win32/taskschd/timetrigger

TASK_TRIGGER_TIME = 1
# The time format is "-MM-DD" "T" "HH:MM:SS" [(+-)"HH:MM"]. For
# example, "1980-01-01T00:00:00+00:00". Local time is used if the
# optional UTC offset is omitted.
TIME_TEMPLATE = "{:%Y-%m-%dT%H:%M:%S}"

trigger = taskDefinition.Triggers.Create(TASK_TRIGGER_TIME)
trigger.Id = "TimeTriggerId"
trigger.Enabled = True
trigger.StartBoundary = TIME_TEMPLATE.format(
datetime.now() + timedelta(seconds=30))
trigger.EndBoundary = TIME_TEMPLATE.format(
datetime.now() + timedelta(minutes=5))
trigger.ExecutionTimeLimit = "PT5M" # 5 minutes

# Add an ExecAction.
# https://learn.microsoft.com/windows/win32/taskschd/actioncollection
# https://learn.microsoft.com/windows/win32/taskschd/execaction

TASK_ACTION_EXEC = 0

action = taskDefinition.Actions.Create(TASK_ACTION_EXEC)
action.Id = "ExecActionid"
action.Path = r"C:\Windows\System32\notepad.exe"

# Register the task to run only in the user's interactive session.
# https://learn.microsoft.com/windows/win32/taskschd/taskfolder

TASK_CREATE_OR_UPDATE = 6
TASK_LOGON_PASSWORD = 1
TASK_LOGON_S4U = 2 # requires admin access
TASK_LOGON_INTERACTIVE_TOKEN = 3
TASK_LOGON_GROUP = 4
TASK_LOGON_SERVICE_ACCOUNT = 5

service.GetFolder("\\").RegisterTaskDefinition(
"Time Trigger Test", taskDefinition, TASK_CREATE_OR_UPDATE,
None, None, TASK_LOGON_INTERACTIVE_TOKEN)
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Need a value from pywin32

2022-06-22 Thread Eryk Sun
On 6/21/22, Steven Manross  wrote:
>
> class WinStationInformation(ctypes.Structure):
> __fields__ = [
> ('ConnectState', ctypes.c_long),
> ('WinStationName', ctypes.wintypes.WCHAR),
> ('LogonId', ctypes.c_ulong),
> ('ConnectTime', ctypes.wintypes.LARGE_INTEGER),
> ('DisconnectTime', ctypes.wintypes.LARGE_INTEGER),
> ('LastInputTime', ctypes.wintypes.LARGE_INTEGER),
> ('LogonTime', ctypes.wintypes.LARGE_INTEGER),
> ('Status', ctypes.c_int()),
> ('Domain', ctypes.wintypes.WCHAR * (17 + 1)),
> ('UserName', ctypes.wintypes.WCHAR * (20 + 1)),
> ('CurrentTime', ctypes.wintypes.LARGE_INTEGER),
> ]

The above definition is incorrect for `WinStationName` and `Status`.
Defining the PROTOCOLSTATUS type for `Status` is tedious. Note also
that the ctypes attribute to set is `_fields_`, not `__fields__`, so
the above struct is actually defined with no fields (i.e. zero size).

Here's an example that defines a get_idle_time() function, based on
the difference between the current time and the last input time in the
session.

import ctypes
from ctypes import wintypes

winsta = ctypes.WinDLL('winsta', use_last_error=True)

WINSTATIONNAME_LENGTH = 32
DOMAIN_LENGTH = 17
USERNAME_LENGTH = 20
MAX_THINWIRECACHE = 4

SERVERNAME_CURRENT = None
LOGONID_CURRENT = -1

# WINSTATIONINFOCLASS
WinStationInformation = 8

# WINSTATIONSTATECLASS
State_Active = 0
State_Connected = 1
State_ConnectQuery = 2
State_Shadow = 3
State_Disconnected = 4
State_Idle = 5
State_Listen = 6
State_Reset = 7
State_Down = 8
State_Init = 9

class TSHARE_COUNTERS(ctypes.Structure):
__slots__ = ()
_fields_ = (
('Reserved', wintypes.ULONG),
)

class PROTOCOLCOUNTERS(ctypes.Structure):
__slots__ = ()
class SPECIFIC(ctypes.Union):
__slots__ = ()
_fields_ = (
('TShareCounters', TSHARE_COUNTERS),
('Reserved', wintypes.ULONG * 100),
)
_fields_ = (
('WdBytes', wintypes.ULONG),
('WdFrames', wintypes.ULONG),
('WaitForOutBuf', wintypes.ULONG),
('Frames', wintypes.ULONG),
('Bytes', wintypes.ULONG),
('CompressedBytes', wintypes.ULONG),
('CompressFlushes', wintypes.ULONG),
('Errors', wintypes.ULONG),
('Timeouts', wintypes.ULONG),
('AsyncFramingError', wintypes.ULONG),
('AsyncOverrunError', wintypes.ULONG),
('AsyncOverflowError', wintypes.ULONG),
('AsyncParityError', wintypes.ULONG),
('TdErrors', wintypes.ULONG),
('ProtocolType', wintypes.USHORT),
('Length', wintypes.USHORT),
('Specific', SPECIFIC),
)


class THINWIRECACHE (ctypes.Structure):
__slots__ = ()
_fields_ = (
('CacheReads', wintypes.ULONG),
('CacheHits', wintypes.ULONG),
)


class RESERVED_CACHE(ctypes.Structure):
__slots__ = ()
_fields_ = (
('ThinWireCache[', THINWIRECACHE * MAX_THINWIRECACHE),
)


class CACHE_STATISTICS(ctypes.Structure):
__slots__ = ()
class SPECIFIC(ctypes.Union):
__slots__ = ()
_fields_ = (
('ReservedCacheStats', RESERVED_CACHE),
('TShareCacheStats', wintypes.ULONG),
('Reserved', wintypes.ULONG * 20),
)
_fields_ = (
('ProtocolType', wintypes.USHORT),
('Length', wintypes.USHORT),
('Specific', SPECIFIC),
)


class PROTOCOLSTATUS(ctypes.Structure):
__slots__ = ()
_fields_ = (
('Output', PROTOCOLCOUNTERS),
('Input', PROTOCOLCOUNTERS),
('Cache', CACHE_STATISTICS),
('AsyncSignal', wintypes.ULONG),
('AsyncSignalMask', wintypes.ULONG),
)


class WINSTATIONINFORMATION(ctypes.Structure):
__slots__ = ()
_fields_ = (
('ConnectState', ctypes.c_long),
('WinStationName', wintypes.WCHAR * (WINSTATIONNAME_LENGTH + 1)),
('LogonId', wintypes.ULONG),
('ConnectTime', wintypes.LARGE_INTEGER),
('DisconnectTime', wintypes.LARGE_INTEGER),
('LastInputTime', wintypes.LARGE_INTEGER),
('LogonTime', wintypes.LARGE_INTEGER),
('Status', PROTOCOLSTATUS),
('Domain', wintypes.WCHAR * (DOMAIN_LENGTH + 1)),
('UserName', wintypes.WCHAR * (USERNAME_LENGTH + 1)),
('CurrentTime', wintypes.LARGE_INTEGER)
)


winsta.WinStationQueryInformationW.restype = wintypes.BOOLEAN
winsta.WinStationQueryInformationW.argtypes = (
wintypes.HANDLE, # ServerHandle
wintypes.ULONG,  # SessionId
ctypes.c_long,   # WinStationInformationClass
wintypes.LPVOID, # pWinStationInformation
wintypes.ULONG,  # WinStationInformationLength
wintypes.PULONG, # pReturnLength
)


def get_idle_time(session_id=LOGONID_CURRENT,
  server_handle=SERVERNAME_CURRENT):
info = WINSTATIONINFORMATION()
rlen = wintypes.ULONG()
if not winsta.WinStationQueryInformationW(
server_handle, session_id, 

Re: [python-win32] Unable to detect shutdown

2022-03-09 Thread Eryk Sun
On 3/9/22, H.Fujii  wrote:
> I'm currently developing a console program in Python 3.9.
> I want to detect the shutdown of Windows and save the data to a file
> safely.
> I created a sample program by referring to the following URL.
> https://docs.microsoft.com/en-us/windows/console/registering-a-control-handler-function
>
> However, the sample program does not detect shutdown
> (CTRL_SHUTDOWN_EVENT) well.

Console control events are sent by the Windows session server,
csrss.exe, by creating a thread in the target process that starts at
CtrlRoutine() in kernelbase.dll. This includes the cancel (i.e.
Ctrl+C), break, and close events that are requested by a console
session host (i.e. conhost.exe or openconsole.exe), as well as the
system-initiated logoff and shutdown events.

The session server sends the logoff and shutdown events to any process
that isn't a GUI process. It doesn't matter whether or not the process
is attached to a console. For example, the service control manager in
services.exe depends on the console shutdown event, even though it is
not a console application and is not attached to a console.

A GUI process is one that's connected to a window station (e.g.
interactive "WinSta0"), with one or more threads connected to a
desktop (e.g. "WinSta0\Default"). Loading user32.dll, directly or
indirectly, connects the process and loading thread to a window
station and desktop (e.g. as set by its STARTUPINFO). In order to
avoid getting converted to a GUI process, a console or background
application has to carefully restrict itself to the base API, avoiding
anything that might end up loading user32.dll, or by spawning a child
process to call anything that loads user32.dll as a remote procedure
call.

Python 3.9+ starts out okay in this regard. But some extension modules
in the standard library -- e.g. _ssl.pyd, _hashlib.pyd, and
_ctypes.pyd -- do not delay-load dependencies that end up loading
user32.dll. Thus, for example, it is currently impossible to use
ctypes to handle the console logoff and shutdown events.

As to win32api, it directly depends on user32.dll as well as
indirectly via other DLLs. I think partitioning out a win32base module
would be a worthy goal, but that's up to the PyWin32 project
developers.

If you can't take the simple route of using console logoff and
shutdown events, then all you can do is fully embrace being a GUI
process. Create a hidden top-level window with a message loop that
handles WM_QUERYENDSESSION [1] and WM_ENDSESSION [2].

---

[1] https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-queryendsession
[2] https://docs.microsoft.com/en-us/windows/win32/shutdown/wm-endsession
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Inheriting a command line - or how to efficiently replace Unix' os.execvpe

2021-05-14 Thread Eryk Sun
On 5/13/21, Sebastian M. Ernst  wrote:
>
> ... but I am not managing to specify `parameters` so that I can actually
> interact with `ssh`. Am I on the wrong track ... ?

It's not clear to me what you need. Do you need to spawn and wait for
an interactive ssh session that's driven normally by the user? Or do
you need expect-like automation of an ssh session?
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Inheriting a command line - or how to efficiently replace Unix' os.execvpe

2021-05-12 Thread Eryk Sun
On 5/12/21, Sebastian M. Ernst  wrote:
>
> It starts the new process more or less like a child, but does not allow it to
> control the command line. Instead, both the Python process and the child
> die. The child merely manages to produce a few lines of output before that.

First let's go over some basic information about consoles in Windows.
Each console session [1] is implemented by a host process, which
currently is either conhost.exe or openconsole.exe (used by Windows
Terminal). Each Windows process can attach to one and only one console
session at a time. A console session doesn't always have a window
(i.e. the CREATE_NO_WINDOW process creation flag). Windows 10 also
allows the console host to operate in pseudoconsole mode, which relays
I/O to another application that implements the console/terminal user
interface, or no UI at all. For example, each tab in Windows Terminal
(wt.exe) is a pseudoconsole session that's hosted by an instance of
openconsole.exe.

A console session doesn't implement the POSIX concept of foreground
and background process groups. Any process that's attached to a
console can read and write from it. Generally when one starts a child
process in the same console session, one either avoids using console
I/O until the child exits, or one simply blocks and does nothing while
waiting for the child to exit. Typically a command-line shell (e.g.
cmd.exe) implements the latter.

> `os.execvpe` is present in the Python standard library on Windows, but
> it does not replace the original (Python) process. Windows is apparently
> lacking relevant POSIX semantics.

os.execve() is implemented internally as the equivalent of
os.spawnve() with P_OVERLAY. I suggest you pretend that it does not
exist. Windows does not support overlaying a new executable image on
the current process, which is what callers reasonably expect from an
exec() call. Instead, with P_OVERLAY the parent process spawns the
child in a new process and then exits. If a grandparent process is
waiting on the parent process, it will resume when the parent exits.
The grandparent and its grandchild will then compete with each other
for console I/O, which is an interleaved mess. You'll get a prompt
from the grandparent, but the text you enter will be read by the
grandchild. Then you'll get the grandchild's prompt, and so on.

> `ssh` opens into a second, new `cmd.exe` window and can be interacted
> with. The original `cmd.exe` window with Python in it remains open,
> Python itself quits, returning control to `cmd.exe` itself.

You're confusing things. cmd.exe is just a console client application.
It has nothing to do with implementing a console session. If `ssh` is
"ssh.bat", then it will execute via cmd.exe. But if it's an executable
binary, either it will allocate a new console session if it doesn't
inherit one (i.e. either the parent has no console, or the process was
created with the CREATE_NEW_CONSOLE or CREATE_NO_WINDOW creation flag)
or it will execute without a console if it's a detached process (i.e.
the process was created with the DETACHED_PROCESS creation flag). You
can pass these creation flags via the `creationflags` parameter of
subprocess.Popen(). CREATE_NO_WINDOW is ignored if paired with either
CREATE_NEW_CONSOLE or DETACHED_PROCESS, and the latter two obviously
cannot be paired with each other.

If you want to reuse the current console session, you'll have to spawn
ssh, wait for it to exit, and proxy its exit status. That's the
closest you can get to using exec*() in a POSIX system.

---

[1] There's an unrelated concept of the active "Console" session in
Windows terminal services. A Windows session contains a "WinSta0"
interactive window station, which has input devices and a display and
contains desktops such as the "Default" desktop, which contain
windows. The "Console" session is the one that's connected to the
physical console, i.e. the physical display, keyboard, and mouse. A
Windows session also has the concept of a graphical shell, which is
typically Windows Explorer. Generally speaking, however, the term
"console" in Windows refers to a sub-session (i.e. subsystem) for
text-based applications, which can exist in any Windows session, even
in the non-interactive "Services" session.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Opening existing memory mapped files with pywin32

2021-02-21 Thread Eryk Sun
On 2/21/21, rhyslloyd1  wrote:
>
> In my head my ideal outcome is being able to map the whole memory mapped
> file and not have to specify a size for it incase it changes size however I
> don't understand the concept of a memory mapped file so I maybe my very
> limited understanding is just incorrect, thanks in advance for any response!

The address space of a process is managed in pages of a given fixed
size (e.g. 4 KiB). The state of each page is tracked in an entry in
the process page table, i.e. a page-table entry (PTE). When a page is
sharable between processes, its PTE in a particular process references
a prototype PTE that manages the overall state of the page (e.g.
whether the page is resident in RAM or paged out to a filesystem
paging file or data file).

The system uses a kernel object called a Section to reference the
prototype PTEs in a block of shared memory. In the Windows API, a
Section is called a "file mapping" because the pages mapped into
memory are always a byte range in a filesystem paging file or data
file. At the time of creation via CreateFileMappingW, a file mapping
can be assigned a name, either a global name for all sessions, or a
local name in the current desktop session. This allows a process to
easily try to open a file mapping (e.g. via OpenFileMapping) and use
it to map all or a subset of the shared pages into its address space
(e.g. via MapViewOfFile).

In your case, I gather that you need to open a file mapping named
"{8BA1E16C-FC54-4595-9782-E370A5FBE8DA}" and map the whole block of
shared memory into the current process, without knowing how large it
is. You'll need to query the size of the mapped region (i.e. the
RegionSize) via VirtualQuery. Preferably also wrap the memory in an
easy interface such as a memoryview, which should automatically unmap
the memory when finalized.

Here's a map_section(name, mode) function that implements this. Note
that for reliable operation it requires memoryview.toreadonly(), which
was added in Python 3.8. The underlying ctypes array allows writing to
readonly memory, which will crash the process with an access
violation. That's avoided in 3.8+ by returning a readonly memoryview
when the shared memory is mapped without write access.

import warnings
import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

FILE_MAP_READ = SECTION_MAP_READ = 0x0004
FILE_MAP_WRITE = SECTION_MAP_WRITE = 0x0002

class MEMORY_BASIC_INFORMATION(ctypes.Structure):
_fields_ = (('BaseAddress', wintypes.LPVOID),
   ('AllocationBase', wintypes.LPVOID),
   ('AllocationProtect', wintypes.DWORD),
   ('PartitionId', wintypes.WORD),
   ('RegionSize', ctypes.c_size_t),
   ('State', wintypes.DWORD),
   ('Protect', wintypes.DWORD),
   ('Type', wintypes.DWORD))

PMEMORY_BASIC_INFORMATION = ctypes.POINTER(MEMORY_BASIC_INFORMATION)

kernel32.OpenFileMappingW.restype = wintypes.HANDLE
kernel32.OpenFileMappingW.argtypes = (wintypes.DWORD, wintypes.BOOL,
wintypes.LPWSTR)

kernel32.MapViewOfFile.restype = wintypes.LPVOID
kernel32.MapViewOfFile.argtypes = (wintypes.HANDLE, wintypes.DWORD,
wintypes.DWORD, wintypes.DWORD, ctypes.c_size_t)

kernel32.UnmapViewOfFile.restype = wintypes.BOOL
kernel32.UnmapViewOfFile.argtypes = (wintypes.LPCVOID,)

kernel32.VirtualQuery.restype = ctypes.c_size_t
kernel32.VirtualQuery.argtypes = (wintypes.LPCVOID,
PMEMORY_BASIC_INFORMATION, ctypes.c_size_t)

class BaseSharedMem(ctypes.Array):
_type_ = ctypes.c_char
_length_ = 0

def __del__(self, *, UnmapViewOfFile=kernel32.UnmapViewOfFile,
warn=warnings.warn):
if not UnmapViewOfFile(self):
warn("UnmapViewOfFile failed", ResourceWarning, source=self)


def map_section(name, mode='r'):
mbi = MEMORY_BASIC_INFORMATION()

if mode == 'r':
access = FILE_MAP_READ
elif mode == 'w':
access = FILE_MAP_WRITE
elif mode in ('rw', 'wr'):
access = FILE_MAP_READ | FILE_MAP_WRITE
else:
raise ValueError('invalid mode')

h = kernel32.OpenFileMappingW(access, False, name)
if not h:
raise ctypes.WinError(ctypes.get_last_error())

try:
address = kernel32.MapViewOfFile(h, access, 0, 0, 0)
if not address:
raise ctypes.WinError(ctypes.get_last_error())
finally:
kernel32.CloseHandle(h)

result = kernel32.VirtualQuery(address, ctypes.byref(mbi),
ctypes.sizeof(mbi))
if not result:
ex = ctypes.WinError(ctypes.get_last_error())
if not kernel32.UnmapViewOfFile(address):
ex2 = ctypes.WinError(ctypes.get_last_error())
raise ex2 from ex
raise ex

array_t = type('SharedMem_{}'.format(mbi.RegionSize),
(BaseSharedMem,), {'_length_': mbi.RegionSize})
mv = memoryview(array_t.from_address(address)).cast('B')

if 'w' not in mode:
return 

Re: [python-win32] Opening existing memory mapped files with pywin32

2021-02-16 Thread Eryk Sun
On 2/16/21, Tim Roberts  wrote:
> Eryk Sun wrote:
>>
>> I'm sorry to say that the only reliable solution is to directly call
>> WinAPI OpenFileMappingW() and MapViewOfFile() using a foreign-function
>> interface (FFI) package such as ctypes or CFFI. I can write up an
>> example if you need help.
>
> Respectfully, I think you have misread the original message.  He is a
> relative beginner, just trying to do a simple file mapping. Based on his
> description, mmap should be able to do what he needs.

Sorry, I'm sure you're right, now that I think about it. I was
responding reflexively based on the OP's code that attempts to
create/open a file mapping with a name and 0 maximum size (i.e.
unknown size; just map it all and query the size), since I've
previously seen people trying and failing to do that with mmap in
various IPC cases. The GUID name didn't help to clarify matters, since
it's just as likely to be generated as a unique object name in the
current session. I also think at unconscious level I didn't want to
assume a confused misreading of the documented function parameters.

Anyway, maybe the discussion will help someone else that's searching
for information on the topic.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Opening existing memory mapped files with pywin32

2021-02-16 Thread Eryk Sun
On 2/16/21, rhyslloyd1 via python-win32  wrote:
>
> I am trying to open a memory mapped file using Python. I originally used the
> "mmap" module from Python however I had issues with it because I had to use
> a fixed size for the file even though my goal was to open an existing file
> of which the size would be unknown.

I'm sorry to say that the only reliable solution is to directly call
WinAPI OpenFileMappingW() and MapViewOfFile() using a foreign-function
interface (FFI) package such as ctypes or CFFI. I can write up an
example if you need help.

In theory, shared_memory.SharedMemory() in the multiprocessing package
could support this case. But in practice it's still based on the mmap
module. It tries to improve the situation by opening and mapping an
existing mapping via WinAPI OpenFileMappingW() [1] and MapViewOfFile()
[2] in order to query the region size via VirtualQuery(), but this
isn't reliable because it returns the size rounded up to the nearest
page boundary. Calling mmap.mmap(-1, size, tagname=name) with this
queried size may fail the internal MapViewOfFile() call with
ERROR_ACCESS_DENIED (5) if the size is bigger than the maximum size of
the file mapping. (The underlying status from the NT system call more
clearly describes the failure as STATUS_INVALID_VIEW_SIZE.)

The problem with the standard library mmap module and PyWin32's
mmapfile is that they're designed to open a mapping only via
CreateFileMappingW() [3]. When this function is called with hFile as
INVALID_HANDLE_VALUE (i.e. use the system paging file) and the maximum
size as 0 (i.e. dwMaximumSizeHigh=0, dwMaximumSizeLow=0), it always
fails with ERROR_INVALID_PARAMETER (87). It does not check whether
lpName is the name of an existing mapping before validating the
parameters and failing. Hypothetically, it could have been written to
check for this case, since the file handle is ignored anyway for an
existing mapping. But it is what it is, and has been so for about 30
years.

A high-level library function needs to be aware of this limitation and
handle it appropriately by implementing an open disposition (e.g.
create-new, open-always, open-existing) that can dictate the use of
OpenFileMappingW() in addition to or instead of CreateFileMapping().

---
[1] 
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-openfilemappinga
[2] 
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile
[3] 
https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

2021-02-10 Thread Eryk Sun
On 2/9/21, Doug Campbell  wrote:
>
> Again, you expand my knowledge!  It seems so obvious now after reading what
> you wrote that I would not be able to get volume disk extents for a physical
> partition but yet this is what I wanted to do because I was attempting to
> find out the partition's offset on the disk.

In the case of a basic disk device, it can have multiple partitions
that each correspond to a volume device, \Device\HarddiskVolume. In
this case, each volume device consists of a single extent on a single
disk.

> The only way I can find to accomplish this is to iterate through each volume

You can get the list of volume GUID names of volume devices that are
registered with the mountpoint manager via FindFirstVolumeW /
FindNextVolumeW. Unfortunately, PyWin32 doesn't wrap the volume-find
functions. They can be called using ctypes. For example:

import ctypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

class HANDLE(ctypes.c_void_p):
def __eq__(self, other):
if hasattr(other, 'value'):
return self.value == other.value
return False

INVALID_HANDLE_VALUE = HANDLE(-1)
ERROR_NO_MORE_FILES = 18

kernel32.FindFirstVolumeW.restype = HANDLE

def list_volumes():
volumes = []
buf = (ctypes.c_wchar * 260)()
hfind = kernel32.FindFirstVolumeW(buf, len(buf))
if hfind == INVALID_HANDLE_VALUE:
raise ctypes.WinError(ctypes.get_last_error())
try:
while True:
# Strip the trailing backslash that FindNextVolumeW appends.
# The trailing backslash is the mountpoint of the filesystem
# that mounts the volume device, but we only want the GUID
# device name.
volumes.append(buf.value.rstrip('\\'))
if not kernel32.FindNextVolumeW(hfind, buf, len(buf)):
error = ctypes.get_last_error()
if error != ERROR_NO_MORE_FILES:
raise ctypes.WinError(error)
break
finally:
kernel32.FindVolumeClose(hfind)
return volumes

I subclassed c_void_p to create the HANDLE type in order to avoid the
otherwise automatic conversion of the return value to a builtin Python
type. This is a simple way around needing to declare argtypes for the
functions that take the find handle (actually a pointer to memory) as
an argument.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl using IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS fails with 'Incorrect function.'

2021-02-09 Thread Eryk Sun
On 2/9/21, Doug Campbell  wrote:
>
> win32file.DeviceIoControl(hDevice,
> winioctlcon.IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
> None, extents, None)
> pywintypes.error: (1, 'DeviceIoControl', 'Incorrect function.')
>
> I have tried with all three of the disks on my system:  \\.\PhysicalDrive0,
> \\.\PhysicalDrive1, and \\.\PhyscialDrive2 but the results are always the
> same.

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS requests the disk extents of a
volume device. Disk devices do not implement this request. A volume
device such as "\\.\C:" can span multiple extents across one or more
physical disks such as "\\.\PhysicalDrive0" and "\\.\PhysicalDrive1".

If the request fails with winerror.ERROR_MORE_DATA, you need to be
prepared to resize the structure according to NumberOfDiskExtents.
ctypes doesn't make this all that easy or convenient at a high level.
What I do is to make the array field private and implement a property
that takes the dynamic length into account. For example:

ANYSIZE_ARRAY = 1

class VOLUME_DISK_EXTENTS(ctypes.Structure):
_fields_ = (('NumberOfDiskExtents', ctypes.c_ulong),
('_Extents', DISK_EXTENT * ANYSIZE_ARRAY))

@property
def Extents(self):
offset = type(self)._Extents.offset
array_t = DISK_EXTENT * self.NumberOfDiskExtents
return array_t.from_buffer(self, offset)

def resize(self):
if self.NumberOfDiskExtents < 1:
self.NumberOfDiskExtents = 1
offset = type(self)._Extents.offset
array_size = ctypes.sizeof(DISK_EXTENT) * self.NumberOfDiskExtents
ctypes.resize(self, offset + array_size)

If you passed `vde = VOLUME_DISK_EXTENTS()`, and the request fails
with winerror.ERROR_MORE_DATA, the call should have set
NumberOfDiskExtents to the required length. Call vde.resize(), and try
again. It's a bit clumsy, but it works.

> hDevice = win32file.CreateFile(
> disk, win32con.GENERIC_READ,
> win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE,
> None, win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL,
> None)

IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS is defined to require
FILE_ANY_ACCESS. This means the desired access and access sharing can
both be 0, and should be since there's no reason to request access
that you don't need that could cause the call to fail with a
permission error (e.g. access denied, or a sharing violation if read
access isn't shared). For example:

volume = r'\\.\C:'
hDevice = win32file.CreateFile(volume, 0, 0, None,
win32file.OPEN_EXISTING, 0, None)

---

Volume Device Names

The base name of a volume device is typically something like
"\Device\HarddiskVolume" or "\Device\CdRom", but these are
automatic names that can vary in the device number N each time a
volume comes online. The mountpoint manager usually will also assign
persistent names to a volume device, which are stored in the registry
if necessary (HKLM\System\MountedDevices) and are set globally in the
"\GLOBAL??" device-alias directory when the volume comes online.
Usually it assigns a GUID name of the form
"Volume{12345678----123456789ABC}". A GUID name is used
when mounting a volume on an existing directory (e.g. mounting a
volume as "C:\Mount\BackupDrive"). It usually also assigns a DOS
drive-letter name such as "C:", which provides the classic DOS volume
mountpoint, such as "C:\".

The "\GLOBAL??" object directory is rarely accessed directly, even in
native NT API programs that have full access to the NT object
namespace. Instead, native NT programs use "\??", a virtual directory
that allows accessing global device symlinks in addition to, and
shadowed by, device symlinks in the caller's logon session device
directory. The SYSTEM logon uses "\GLOBAL??" for its logon session
devices, so it always creates and accesses global device aliases.

In the Windows API, the NT API "\??\" prefix maps to "\\.\" or "\\?\"
UNC-style paths. The former is more common if just the device is
accessed, and the latter is more common for a filesystem path. Thus to
access the "C:" volume device directly, programs typically use
"\\.\C:". Note that just "C:" by itself is a DOS drive-relative path
that expands to the working directory in the mounted filesystem, which
defaults to the root directory. So passing "C:" as the path to open is
actually a reference to a filesystem directory, not to the volume
device, which obviously won't work.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl calls respond with parameter incorrect

2021-02-09 Thread Eryk Sun
On 2/9/21, Doug Campbell  wrote:
>
> That was exactly what I needed.  I will have to read up on the _pack_
> directive to understand it but for now things are running the way they
> should be.

I'm glad I could help. Normally padding is added between fields of a
struct in order to support aligned access for the data type of each
field. Setting the `_pack_ = 1` attribute forces single-byte
alignment, and thus no padding bytes are added.

> I did have one typo in what I originally pasted that made its way into what
> you provided.
>
> ('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE),
>
> should actually be
>
> ('volumeID', ctypes.c_char * VOLUME_ID_SIZE),

Sorry, I did actually scan over the fields to check the types against
the C definition, but I somehow missed that extra "w".
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] DeviceIOControl calls respond with parameter incorrect

2021-02-08 Thread Eryk Sun
On 2/8/21, Doug Campbell  wrote:
> In my python 2 script, I am trying to connect to the VeraCrypt device driver
> to get some information on my mounted volumes.

The VeraCrypt repo on GitHub [1] indicates that all structures are
defined with #pragma pack(1). In ctypes this is the _pack_ directive.
Try the following:

import ctypes
import winioctlcon
import win32file

def VC_IOCTL(CODE):
return winioctlcon.CTL_CODE(winioctlcon.FILE_DEVICE_UNKNOWN,
0x800 + CODE, winioctlcon.METHOD_BUFFERED,
winioctlcon.FILE_ANY_ACCESS)

VC_IOCTL_GET_MOUNTED_VOLUMES = VC_IOCTL(6)
VC_IOCTL_GET_VOLUME_PROPERTIES = VC_IOCTL(7)
VC_IOCTL_GET_BOOT_ENCRYPTION_STATUS = VC_IOCTL(18)
VC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES = VC_IOCTL(22)
VC_IOCTL_EMERGENCY_CLEAR_KEYS = VC_IOCTL(41)

MAX_PATH = 260
VOLUME_LABEL_LENGTH = 33 # 32 + null
VOLUME_ID_SIZE = 32
WIN32_ROOT_PREFIX DRIVER_STR = r'\\.\VeraCrypt'

class VOLUME_PROPERTIES_STRUCT(ctypes.Structure):
_pack_ = 1
_fields_ = (
('driveNo', ctypes.c_int),
('uniqueId', ctypes.c_int),
('wszVolume', ctypes.c_wchar * MAX_PATH),
('diskLength', ctypes.c_uint64),
('ea', ctypes.c_int),
('mode', ctypes.c_int),
('pkcs5', ctypes.c_int),
('pkcs5Iterations', ctypes.c_int),
('hiddenVolume', ctypes.c_int),
('readOnly', ctypes.c_int),
('removable', ctypes.c_int),
('partitionInInactiveSysEncScope', ctypes.c_int),
('volFormatVersion', ctypes.c_uint32),
('totalBytesRead', ctypes.c_uint64),
('totalBytesWritten', ctypes.c_uint64),
('hiddenVolProtection', ctypes.c_int),
('volFormatVersion', ctypes.c_int),
('volumePim', ctypes.c_int),
('wszLabel', ctypes.c_wchar * VOLUME_LABEL_LENGTH),
('bDriverSetLabel', ctypes.c_int),
('volumeID', ctypes.c_wchar * VOLUME_ID_SIZE),
('mountDisabled', ctypes.c_int))


prop = VOLUME_PROPERTIES_STRUCT(driveNo = ord('F') - ord('A'))

hDevice = win32file.CreateFile(WIN32_ROOT_PREFIX DRIVER_STR, 0, 0, None,
win32file.OPEN_EXISTING, 0, None)
try:
info = win32file.DeviceIoControl(hDevice,
VC_IOCTL_GET_VOLUME_PROPERTIES, prop, prop)
finally:
hDevice.close()

---
[1] https://github.com/veracrypt/VeraCrypt/blob/master/src/Common/Apidrvr.h
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] how to code pywin32 run existing window task scheduler?

2021-01-14 Thread Eryk Sun
On 1/14/21, Tim Roberts  wrote:
> On Jan 13, 2021, at 8:07 PM, Pongthorn Sangkaphet
>
>> I have tried already but error
>>
> That’s 0x80070005, which is ERROR_ACCESS_DENIED.  Have you tried running
> this from an elevated process?

The default security descriptor of a task grants all access to
administrators, and the principal user of the task is granted read
access. Implicitly, however, the principal can execute a task as long
the task security grants read access. Because the principal user can
execute a task, this allows an administrator to configure a task to
run elevated (i.e. with highest privileges) as the administrator user,
which bypasses UAC (e.g. when executed via schtasks /run from a
restricted security context). If the principal user has the batch
logon right, then the task can also run in the services session (i.e.
the option to run whether or not the user is logged on), but that's
not pertinent to the question of execute access.

If you need to allow some other user to read and execute a task (e.g.
to bypass UAC in the context of the principal user), then you'll need
to grant that user file read (FR) and execute (FX) access. Begin by
retrieving the task's DACL_SECURITY_INFORMATION (4) in SDDL form from
GetSecurityDescriptor [1]. You can convert this string to an SD record
to work with it programmatically, or simply append an allow ACE in
SDDL form, such as "(A;;FRFX;;;)". Then write the new DACL
via SetSecurityDescriptor.

---
[1] https://docs.microsoft.com/en-us/windows/win32/taskschd/registeredtask
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32process.CreateProcess: '%1 is not a valid Win32 application.'

2020-12-09 Thread Eryk Sun
On 12/9/20, Niko Pasanen  wrote:
>
> So the problem might be that on the Person C's computer, there is actually
> a file called "first" that is creating the error. The reason why I was
> using win32process.CreateProcess like this was that I'm actually using
> pywinauto, which uses  win32process.CreateProcess. I'll have to still test
> this, but apparently this will fix some possible bugs. Thank you!

If you can't use lpApplicationName (preferred if the exact path is
known) because you don't control the code, then just manually quote
the executable path, e.g. pywinauto_call( f'"{exepath}"', ...).

> The results for assoc and ftype are the same on my machine (should be tested
> on person C's pc, too).

The assoc and ftype commands have nothing to do with CreateProcess, so
the suggestion to inspect them isn't particularly relevant. The only
way the desktop shell's ".exe" file association might be relevant is
if it's customized in some way to allow ShellExecuteEx to succeed even
though CreateProcess on its own fails. The default association for
".exe" files defines a template command `"%1" %*`, which tells
ShellExecuteEx to just call CreateProcess with the path of the
executable and the command-line arguments. In theory the ".exe"
association can be changed to proxy process creation with a launcher,
but that would be a wonky thing to do, and it's not relevant to
calling CreateProcess directly.

FYI, assoc and ftype are old CMD shell builtin commands from NT 4.0
(1996) that get and set file associations and the "open" action of
progids (f[ile]type program identifiers) in "HKLM\Software\Classes".
These settings *might* be used by ShellExecuteEx. Nowadays these two
commands are outdated and should not be relied on.
"HKCU\Software\Classes" takes precedence, which these commands do not
show. Numerous other registry keys for primary and fallback settings
have also been added over the years, which these two commands also do
not show. And, what's even more important is that the desktop shell
was changed to cache and permanently store user choices separate from
the default association selection algorithm. Cached choices and the
pinned "UserChoice" are stored in
"HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\".
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32process.CreateProcess: '%1 is not a valid Win32 application.'

2020-12-08 Thread Eryk Sun
On 12/8/20, Niko Pasanen  wrote:
>
> path_to_exe = Path(r"C:\path to some\folder\some.exe")

FYI, the constructor of pathlib.Path supports forward slash as the
path separator, which avoids the need to use a raw string. A
WindowsPath instance uses backslash when converting back to a string.

> (h_process, _, dw_process_id, _) = win32process.CreateProcess(
> None,  # module name
> str(path_to_exe),  # command line

Since you just have an executable path, pass it in lpApplicationName
and leave lpCommandLine as None. This is simpler than manually quoting
the path.

If the string in lpCommandLine is an unquoted executable path, Windows
will consume spaces in it until it finds a non-directory match. For
example, if it's r"C:\path to\some folder\some.exe", it will use
r"C:\path to\some" or r"C:\path to\some.exe" if either exists and is
not a directory. If the matching file isn't a valid PE image, it does
not continue parsing the command line to consume the next space as
"some folder". It simply fails the call with ERROR_BAD_EXE_FORMAT.

> 1) The following DLLs on *both* machines with same path (mine & Person C)
> -  "python.exe"

ERROR_BAD_EXE_FORMAT occurs when mapping the executable image into the
process VM during the NtCreateUserProcess system call. At this stage,
no dependent DLLs are mapped into the process. That occurs after the
process has already been created, during process initialization. If it
were a dependent DLL problem, then CreateProcess would succeed, but
the child process would abnormally terminate with a status code such
as STATUS_INVALID_IMAGE_FORMAT (0xC07B).
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Help with PySECURITY_DESCRIPTOR

2020-11-13 Thread Eryk Sun
On 10/27/20, momc...@bojinov.info  wrote:
>
> I m trying to store file's acl along with the backup of the file and then
> restore it on the same system

Consider using BackupRead() and BackupWrite() from the win32file
module. These functions support backup and restore of data streams
(default and alternate data streams), attributes, extended attributes,
reparse data, object ID, and security.

GENERIC_READ access includes the READ_CONTROL access that's required
for reading most file security, but GENERIC_WRITE access isn't
sufficient for restoring file security. Writing discretionary
access-control entries and resource attributes requires WRITE_DAC
access. Writing the owner, group, and mandatory label requires
WRITE_OWNER access.  Reading and writing audit entries and writing
central-access-policy identifier entries requires
ACCESS_SYSTEM_SECURITY access, which requires enabling
SeSecurityPrivilege. Typically use GENERIC_WRITE | WRITE_DAC |
WRITE_OWNER.

In general you should backup and restore files using an elevated
administrator account. Enable SeBackupPrivilege and SeRestorePrivilege
in the process access token via OpenProcessToken,
LookupPrivilegeValue, and AdjustTokenPrivileges, found in the
win32security module. Open files with FILE_FLAG_BACKUP_SEMANTICS. This
ensures access in most cases when the backup and restore privileges
are enabled. The restore privilege also allows setting the file owner
to the arbitrary owner of the source file instead being limited to the
current user. Using an elevated logon (high integrity level) also
allows restoring a high integrity level mandatory label on the
destination file in case the source file has mandatory access control
that denies write-up, read-up, or execute-up access.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Very unimportant bug report.

2020-06-18 Thread Eryk Sun
On 6/18/20, Vernon D. Cole  wrote:
> I am testing the installation of our newest version 228 of pywin32.
>
> When attempting to install pywidn32 on the 32-bit version of Python
> 3.8 on *Windows 7 *32 bit, I get the following error: "The procedure
> entry point ucrtbase.terminate could not be located in the dynamic
> link library api-ms-win-crt-runtime-l1-1-0.dll."

Python 3.8 officially supports Windows 7, but the OS has to be kept
updated (e.g. KB2533623 is required for new loader flags and
AddDllDirectory). I'd check optional Windows updates for a C runtime
update. If that doesn't fix the problem, an issue should probably be
opened -- at least for tracking.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] pure python way to open a file with write deny for others

2020-03-06 Thread Eryk Sun
On 3/6/20, Robin Becker  wrote:
>
> OK I want to read the (small) file completely. The other process may try to
> re-write the file while I am reading it.

I thought the other process already had the file open for writing, but
apparently you just want to deny anyone the right to open the file
with write access while you're reading it. If the file is already open
with write access, you'll have to periodically retry opening it until
you can get read access without sharing write access.

Alternatively, you can use regular read-write sharing, but lock the
file data via msvcrt.locking. This doesn't deny opening the file for
write access, but any attempt to write to a locked region will fail
with a locking violation. The locked region can encompass the entire
file. (File locking in Windows doesn't prevent deleting or renaming a
file, but the default share mode already prevents that.)

> Of course the other process may adopt a completely orthogonal scheme of
> opening with a different name and then renaming,

Normally, open files in Python cannot be deleted or renamed until
they're closed. This is the case as long as you have the file opened
with just read or read-write sharing. Within the standard library,
only the low-level open flag O_TEMPORARY uses shared delete/rename
access.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] pure python way to open a file with write deny for others

2020-03-05 Thread Eryk Sun
On 3/5/20, Robin Becker  wrote:
> I want to be able to read a windows file which is being periodically written
> by another process.

I'm having difficulty reconciling this sentence with the subject line.
If you want to open a file for reading that's already open for
writing, then the open has to share write access, not deny it (where
to "deny" access means to not share access) [1]. That's not an issue
since Python shares read and write access when opening a file. (Note
that the share mode applies to all opens. It is not related to
processes, so whether it's another process or the current process
that's writing to the file is irrelevant to the problem.)

I wouldn't use FILE streams in Python 3. I'd pass an `opener` to
Python `open` that uses ctypes or PyWin32 to get a handle via
`CreateFileW`, and then msvcrt.open_osfhandle to wrap the handle with
a C file descriptor. The opener function signature is opener(filename,
flags) -> fd.

[1]: 
https://docs.microsoft.com/en-us/windows/win32/fileio/creating-and-opening-files
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32api.FindFiles hangs (was COM registration hangs up: 32 bit Python 3.7 on 64 bit Win 7)

2018-12-19 Thread eryk sun
On 12/19/18, Boylan, Ross  wrote:
> Eryk, thank you for that very detailed explanation.  The directory is on a
> network share; is NtQueryDirectoryFile[Ex] still the call that would be made
> for it?

It's the only system call to scan a directory. It's implemented in the
I/O manager by sending an IRP_MJ_DIRECTORY_CONTROL:
IRP_MN_QUERY_DIRECTORY request to the device stack. In this case the
requested directory information is FileBothDirectoryInformation.
Locally, the filesystem for a network share is the MUP device
(multiple UNC provider), which proxies access to a redirector such as
SMB (mrxsmb). Since the same call works from other systems, it's
probably safe to assume the problem is in the local device stack and
not the server on the other end of the wire.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32api.FindFiles hangs (was COM registration hangs up: 32 bit Python 3.7 on 64 bit Win 7)

2018-12-19 Thread eryk sun
On 12/18/18, Boylan, Ross  wrote:
> BTW, I installed Visual Studio and wrote a little C++ program that exercised
> FindNextFile.  It failed in exactly the same way (returns same filename
> forever if on a network drive and search spec has the exact file name), but
> only when run from the original machine.

The find-file handle returned by FindFirstFile[Ex] and used by
FindNextFile is a pointer to a structure that includes the directory
handle, a pointer to a 4 KiB file-record buffer (unless the caller
requests FIND_FIRST_EX_LARGE_FETCH, which uses a 64 KiB buffer), and a
pointer to the current entry's offset in the buffer. The buffer gets
filled via the native system call NtQueryDirectoryFile[Ex]. The
NextEntryOffset field of each variable-sized entry is used to traverse
the buffer. The offset value is 0 in the last record.

As the FindNextFile calls traverse the buffer, the current-entry
pointer is updated for the next call. At the final entry, the pointer
gets reset back to the base address. In this case, the next
FindNextFile call will scan in a new batch of file records via
NtQueryDirectoryFile[Ex]. This continues until the system call returns
STATUS_NO_MORE_FILES (0x8006), in which case FindNextFile fails
with the last error set to ERROR_NO_MORE_FILES (18). This is the
normal way a directory scan is completed.

Here's my guess about what's going wrong in your case.

NtQueryDirectoryFile[Ex] can fail with STATUS_BUFFER_OVERFLOW
(0x8005) if the buffer isn't large enough for a complete entry. In
practice this shouldn't occur because the buffers used by
FindFirstFile[Ex] and FindNextFile are large enough for at least one
complete entry. We might have a filesystem that, on subsequent calls,
stores a partial last entry in the buffer and fails with
STATUS_BUFFER_OVERFLOW. FindNextFile handles this case by forgetting
the partial entry, which it does by setting the NextEntryOffset field
to 0 in the previous entry, if any. Then, even if there's only a
single entry, it continues past the overflow error to return the
current entry. When NtQueryDirectoryFile[Ex] is called again, it
should resume at the entry that was partially returned in the previous
call. But if it was only a single entry, and the buffer-overflow was
due to a bug in the device stack, then we'll scan the same entry again
with the same buffer-overflow error, and we're stuck in an infinite
loop.

Probably this bug is due to a filter driver in the device stack,
rather than the filesystem driver.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Reg. taking folder ownership

2018-08-28 Thread eryk sun
On Tue, Aug 28, 2018 at 5:03 AM, Goku Balu  wrote:
>> Eryk wrote:
>> This call will succeed even if one or more of the privileges wasn't
>> modified. In this case GetLastError() returns ERROR_NOT_ALL_ASSIGNED
>> (1300). This will be the case if you try to enable the take-ownership
>> and restore privileges for a UAC restricted token.
>
> Thanks Eryk for responding. Yes it failed with 1300 and I'm running the code
> from another admin account. But I think it's not a restricted account. I
> could run any exe with Admin elevated privilege by right clicking and
> choosing the option from context menu.

Under UAC, administrators get logged on with two tokens. The one that
LSA returns is a limited token with medium integrity level, from which
administrative privileges have been stripped and for which the
administrators group is enabled only for deny access checks. This
limited token is paired with an elevated token, which has high
integrity, full administrative privileges, and the administrators
group enabled for both allow and deny access checks. The elevated
token can only be obtained by a user with SeTcbPrivilege, such as the
SYSTEM account. Creating an elevated process is commonly handled by
the Application Information service, which runs as SYSTEM. It displays
the consent dialog on the secure (Winlogon) desktop, gets the elevated
token, and creates the process via CreateProcessAsUser.

Run Python elevated, enable SeRestorePrivilege, and use
GetNamedSecurityInfo and SetNamedSecurityInfo. This will allow
modifying the owner or DACL regardless of the current DACL, even if no
access is specifically granted to administrators.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Reg. taking folder ownership

2018-08-27 Thread eryk sun
On Mon, Aug 27, 2018 at 6:23 AM, Goku Balu  wrote:
>
> My use case is this. Folder1 is created by Admin1 and ACL is set by Admin1.
> Now Admin2 wants to change the ACL. I think we have two options here
> 1) Take folder ownership and the do the changes
> 2) Take elevated privileges for Admin2 account and add/remove ACL entries

If the current DACL doesn't grant Admin2 the right to change the owner
or change the DACL (i.e. to acquire said right), then you'll need to
either enable SeTakeOwnershipPrivilege or enable SeRestorePrivilege
with backup semantics. Under UAC restriction, an administrator only
has these privileges when elevated, so option (2) is your only choice.

SeRestorePrivilege also allows setting the owner to an arbitrary user
or group. Otherwise you can only set the owner to either the current
user or any of the user's groups that have the group-owner flag (e.g.
the administrators group).

> win32security.AdjustTokenPrivileges(handle, 0, new_privs)

This call will succeed even if one or more of the privileges wasn't
modified. In this case GetLastError() returns ERROR_NOT_ALL_ASSIGNED
(1300). This will be the case if you try to enable the take-ownership
and restore privileges for a UAC restricted token.

> fs = win32security.GetFileSecurity(
> path, win32security.OWNER_SECURITY_INFORMATION)
> fs.SetSecurityDescriptorOwner(owner_sid, True)
>
> win32security.SetFileSecurity(
> path, win32security.OWNER_SECURITY_INFORMATION, fs)

Use GetNamedSecurityInfo and SetNamedSecurityInfo instead. These newer
functions handle inheritance correctly. They also open the file with
backup semantics. The I/O manager grants all requested modify access
(including write-owner) if backup semantics is combined with
SeRestorePrivilege. In this case you don't need
SeTakeOwnershipPrivilege.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Where is MakeAbsoluteSD?

2018-06-28 Thread eryk sun
On Thu, Jun 28, 2018 at 4:49 AM, Rob Marshall  wrote:
> Hi,
>
> At the moment I don't need it at all, but when I use the smbcacls
> utility from Samba on a Linux system and have it print the security
> descriptor as an SDDL it gives me something like:
>
> O:S-1-5-21-3327876616-1579407131-3503203118-500G:S-1-5-21-3327876616-
> 1579407131-3503203118-513D:P(A;;0x001e01ff;;;S-1-5-21-3327876616-
> 1579407131-3503203118-500)(A;;0x00120089;;;S-1-5-21-3327876616-
> 1579407131-3503203118-513)(A;;0x00120089;;;WD
>
> That's what I was hoping to get so that I could print it out.
> Currently it doesn't matter because I can use smbcacls to get what I
> want.

There is no such thing as an absolute format SDDL string. Do you
understand that "absolute" in this case is referring to absolute
pointer addresses for the SD components *in memory*? This is opposed
to the relative offset format that's used when marshaling the
structure to disk or over the wire. The distinction is completely
irrelevant to the SDDL string representation of an SD. The conversion
from SDDL to SD always creates a self-relative format SD. It's also
not relevant within the PyWin32 framework, since a
PySECURITY_DESCRIPTOR is stored in self-relative format and
automatically converts to absolute format whenever that's required.
The boring details are handled for you automatically.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Where is MakeAbsoluteSD?

2018-06-27 Thread eryk sun
On Wed, Jun 27, 2018 at 3:14 PM, Rob Marshall  wrote:
>
> I saw that, my problem is that I'm trying to use
> win32security.ConvertSecurityDescriptorToStringSecurityDescriptor()
> which is returning a self-relative SDDL and I need one that is
> absolute. Is there a flag that I can set and get an absolute SDDL from
> that function? In the Microsoft documentation for
> ConvertSecurityDescriptorToStringSecurityDescriptor() it says, as to
> what it "returns":

Again, it doesn't matter what form the WinAPI function returns.
Whatever it is, the PySECURITY_DESCRIPTOR object internally converts
it to self-relative format. Why do you need an absolute format SD?
Whatever the reason you'll need to use ctypes for part of the problem.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Where is MakeAbsoluteSD?

2018-06-27 Thread eryk sun
On Wed, Jun 27, 2018 at 5:04 AM, Rob Marshall  wrote:
>
> I'm trying to convert a self-relative security descriptor, i.e. the
> type that is returned by
> win32security.ConvertStringSecurityDescriptorToSecurityDescriptor(),
> to an absolute SD but I can't seem to find the MakeAbsoluteSD()
> function. Where is it?

A PySECURITY_DESCRIPTOR object is stored in self-relative format.
Internally it's converted to absolute format for functions that
require it, such as SetSecurityDescriptorDacl. See the source:

https://github.com/mhammond/pywin32/blob/b223/win32/src/PySECURITY_DESCRIPTOR.cpp
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] pywintypes.error: (5, 'RegSetValueEx', 'Access denied')

2018-03-15 Thread eryk sun
On Thu, Mar 15, 2018 at 8:44 AM, Robin Kluth  wrote:
>
> I use Python 2.7 on win10 and pywin32 223. I want to log messqages to the
> EventLog:
>
> dllname = os.path.dirname(__file__)
> ntl = NTEventLogHandler("Python Logging Test", dllname=dllname)

The Python module should not be used for dllname. This needs to be a
PE image (e.g. PYD, DLL, EXE) that has an embedded message table.
NTEventLogHandler currently just hard codes using event ID 1, which is
"%1\r\n" in win32evtlog.pyd or win32service.pyd. The "%1" insert gets
replaced by the message text when the log record is formatted.

In your case, just leave it as the default.

> Traceback (most recent call last):
>   File "fpp_user_import.py", line 44, in 
> ntl = NTEventLogHandler("Python Logging Test", dllname=dllname)
>   File "C:\Python27\lib\logging\handlers.py", line 987, in __init__
> self._welu.AddSourceToRegistry(appname, dllname, logtype)
>   File "C:\Python27\lib\site-packages\win32\lib\win32evtlogutil.py", line
> 42, in AddSourceToRegistry
> msgDLL)
> pywintypes.error: (5, 'RegSetValueEx', 'Zugriff verweigert')
>
> If I run the script with Admin privs, it is working.
>
> How to log as "normal" user? Am I missing something?

This should be enhanced in NTEventLogHandler. It's not the end of the
world if this key doesn't get created. The event viewer will just show
a warning that the event ID can't be found for the given source. Also,
when installed an application can request elevation to register the
event log source. Given this, I suggest you work around the problem
with a subclass that ignores this error. For example:

import logging
import logging.handlers

class NTEventLogHandler(logging.handlers.NTEventLogHandler):
__doc__ = logging.handlers.NTEventLogHandler.__doc__
def __init__(self, appname, dllname=None, logtype="Application"):
logging.Handler.__init__(self)
self.appname = appname
self.dllname = dllname
self.logtype = logtype
try:
import win32evtlogutil, win32evtlog, winerror
except ImportError:
print('The Python Win32 extensions for NT (service, event '
  'logging) appear not to be available.')
self._welu = None
return
self._welu = win32evtlogutil
self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
self.typemap = {
logging.DEBUG   : win32evtlog.EVENTLOG_INFORMATION_TYPE,
logging.INFO: win32evtlog.EVENTLOG_INFORMATION_TYPE,
logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
logging.ERROR   : win32evtlog.EVENTLOG_ERROR_TYPE,
logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
}
try:
win32evtlogutil.AddSourceToRegistry(appname, dllname, logtype)
except win32evtlogutil.error as e:
if e.winerror != winerror.ERROR_ACCESS_DENIED:
raise
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] playing with scintillacon

2017-12-15 Thread eryk sun
On Fri, Dec 15, 2017 at 9:10 PM, Kurt Eilander  wrote:
> Ok, I found scintilla.dll in the directory above scintillacon.py, but it
> came with pywin32, so it *should* be the correct one.
> DLL inspector says it's a 64-bit, which is correct for my os.
>
> I copy that dll, and indeed, all binaries in that directory (just to be
> sure) into my program directory and it still does not work!

Try importing win32api in the same context. If that fails, check PATH
for pywintypesXY.dll:

where pywintypes*.dll

> I have no idea what could be wrong.  Oh!  To know what that little %1 means!

System error message include inserts such as %1 for when errors and
exceptions are shown by the system itself. All Python has in this case
is the error code, ERROR_BAD_EXE_FORMAT. So when it calls
FormatMessage, it uses the flag FORMAT_MESSAGE_IGNORE_INSERTS, as it
should.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] os.remove not deleting a file

2017-11-05 Thread eryk sun
On Tue, Oct 31, 2017 at 1:16 PM, Henk Zevenhuizen
 wrote:
>
> Then i get the message: "after", the file is still there and i cannot delete
> the through windows explorer (permission denied)
> the only way to delete the file is killing python

Does Explorer explicitly tell you "permission denied" or "file access
denied", or is it a "sharing violation" or "file in use" error?

The common case is a sharing violation (error 32). Deleting a file
requires opening it with delete (DELETE) access. Whether or not that's
allowed depends in part on how the file is already open. Filesystems
maintain counts of the number of times a file is open; the number of
opens that have read (or execute), write (or append), or delete
access; and the number that share read (or execute), write (or
append), or delete access. In order for an open with delete access to
not fail with a sharing violation, all previous opens must share
delete access. Also, if any previous open has delete access, then the
current open has to share delete access. IMO, access sharing should
not be called a 'lock'. It could be confused with Windows file
locking, which is a completely different system. File locking does not
prevent file deletion.

For example, Python defaults to opening files with read and write
sharing, but not delete sharing:

>>> fd = os.open('test.txt', os.O_CREAT)
>>> os.remove('test.txt')
Traceback (most recent call last):
  File "", line 1, in 
WindowsError: [Error 32] The process cannot access the file
because it is being used by another process: 'test.txt'

Once the file handle is closed, os.remove (WinAPI DeleteFile) succeeds:

>>> os.close(fd)
>>> os.remove('test.txt')
>>> os.listdir('.')
[]

If, on the other hand, Explorer is subsequently being denied delete
access (error 5), then maybe your Python program actually has the file
open with delete sharing. In this case, initially 'deleting' the file
will succeed, but it won't be unlinked, and re-opening the file will
fail with access denied.

For example, the O_TEMPORARY flag opens a file with delete sharing:

>>> fd = os.open('test.txt', os.O_CREAT | os.O_TEMPORARY)
>>> os.remove('test.txt')

DeleteFile succeeds this time, but in Windows that only means the
file's delete disposition is set. This is a flag on the underlying
file/stream control block (FCB or SCB) that tells the filesystem to
unlink the file when all existing references to it have been closed.
We still have a handle open, so the file is not unlinked, as you can
see via listdir:

>>> os.listdir('.')
['test.txt']

When the delete disposition is set, it's impossible to re-open the
file for any access, even the most basic access to stat() file
metadata:

>>> os.stat('test.txt')
Traceback (most recent call last):
  File "", line 1, in 
WindowsError: [Error 5] Access is denied: 'test.txt'

The file is unlinked after the handle is closed:

>>> os.close(fd)
>>> os.listdir('.')
[]

That would have happened even without calling os.remove because
O_TEMPORARY flags the opened file as delete-on-close.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] CreateDesktop() and displaying a window on it

2017-04-07 Thread eryk sun
On Fri, Apr 7, 2017 at 4:35 PM, Tim Roberts  wrote:
> I see Eryk provided the explanation, but all I had to do to make your
> example work was add
> hDesk.SetThreadDesktop()
> just after your SwitchDesktop call.

That didn't work for me before. It was causing my Windows 10 system to
get stuck on a blank desktop. The SetThreadDesktop call was failing,
which I assumed was because tkinter was automatically creating an
invisible window or hook in the main thread. That's why I used a new
thread as a workaround. But just now it worked. Go figure.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] CreateDesktop() and displaying a window on it

2017-04-06 Thread eryk sun
On Thu, Apr 6, 2017 at 7:38 PM, Kurt Eilander  wrote:
> Hey all,
>
> I'm trying to get system modal functionality like the UAC dialog.
>
> According to http://developex.com/blog/system-modal-back/ the thing to do is
> to create and switch to a new desktop.
>
> Therefore, I'm doing:
>
> hDeskOld=win32service.GetThreadDesktop(win32api.GetCurrentThreadId())

SwitchDesktop affects which desktop has input focus. SetThreadDesktop
affects which desktop is used by the current thread. You need to open
a handle to the current input desktop to switch back to it at the end.

I'm far from a Tk expert. I don't know how to close out the resources
it uses to be able to call SetThreadDesktop. I'm not going to bother
figuring this out to get this working on the main thread. I'll just
create a new thread; pass it the handle for the new desktop; save the
existing input desktop; set the thread desktop; and switch the input
desktop. Then on teardown switch back to the original input desktop
and close the desktop handles.

Since I can't reliably call SetThreadDesktop to restore the worker
thread's desktop before the thread exits, there's some asynchronous
overlap when the thread and its resources haven't been destroyed yet.
To address the potential for getting ERROR_BUSY when calling
CloseDesktop from the main thread, I'm adding a retry loop that
defaults to 10 attempts, with a delay of 0.1s. It shouldn't need more
than 2 attempts.

import sys
import time
import tkinter
import threading

import winerror
import win32con
import win32api
import win32service

def close_desktop(hDesk, numtries=10):
for i in range(numtries):
try:
return hDesk.CloseDesktop()
except win32service.error as e:
if e.winerror != winerror.ERROR_BUSY or i == numtries - 1:
raise
time.sleep(0.1)

def dialog(hDesk, title='SysModalDialog', message='', img=None):
hDeskInputOld = win32service.OpenInputDesktop(0, False,
win32con.DESKTOP_SWITCHDESKTOP)
try:
hDesk.SetThreadDesktop()
hDesk.SwitchDesktop()
try:
root = tkinter.Tk()
#root.mainloop()
app = SysmodalDialog(root, title, message, img)
app.mainloop()
finally:
hDeskInputOld.SwitchDesktop()
finally:
close_desktop(hDeskInputOld)

def main():
hDesk = win32service.CreateDesktop("SysModalDesktop", 0,
win32con.GENERIC_ALL, None)
try:
t = threading.Thread(target=dialog, args=(hDesk,))
t.start()
t.join()
finally:
close_desktop(hDesk)
return 0

if __name__ == '__main__':
sys.exit(main())
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32api handle incompatible with ctypes?

2017-03-04 Thread eryk sun
On Sun, Mar 5, 2017 at 12:16 AM, Kurt Eilander  wrote:
>
> I'm having another problem.  I'm wanting to get the size of a dll resource,
> but...
>
> When I do:
> try:
> hLib=win32api.GetModuleHandle(fileName)
> except:
> hLib=win32api.LoadLibrary(fileName)
> if hLib==None:
> raise WindowsError('File not found, '+fileName)
> hResInfo=ctypes.windll.kernel32.FindResourceW(hLib,index,type)
> size=ctypes.windll.kernel32.SizeofResource(hLib,hResInfo)
>
> It throws:
> hResInfo=ctypes.windll.kernel32.FindResourceW(hLib,index,type)
> ctypes.ArgumentError: argument 1: :
> long int too long to convert
>
> Almost like ctypes doesn't like the win32api handle.
>
> My machine is 64 bit.  Is that what ctypes is not liking?  Is there a way
> around it?

The default conversion for integer arguments is to a 32-bit C int. The
implementation calls PyLong_AsLong, for which a 64-bit address can
raise an overflow exception on Windows since a C long is always 32-bit
on this platform. You're lucky to get an exception. The problem is
worse on Unix systems that have a 64-bit C long. The conversion
doesn't overflow, but ctypes itself silently casts the address to a
32-bit int. Invariably this causes a segfault, either directly or
indirectly due to stack or heap corruption.

It looks like you're also not setting restype to a pointer type for
FindResourceW. Like with parameters, the default integer conversion
type for a function result is a 32-bit C int. A 64-bit result will be
silently truncated on Windows as well. Kernel and User handles never
exceed 32-bit values, but don't assume that's true of all handles.
HMODULE handles break this rule, as do others. Always assume a handle
type requires the full range of a pointer.

Here are some general suggestions. Use ctypes.WinDLL instead of
ctypes.windll (the latter was a bad design for multiple reasons). Load
the library with the option use_last_error=True, unless you're working
with NT or COM libraries that return NTSTATUS or HRESULT values.
Always set the function prototype -- at least argtypes; restype if the
function returns a pointer type; and preferably an errcheck function
that raises idiomatic exceptions.

For example:

import winerror
import win32api
import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

def _check_zero(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args

kernel32.FindResourceW.errcheck = _check_zero
kernel32.FindResourceW.restype = wintypes.HRSRC
kernel32.FindResourceW.argtypes = (
wintypes.HMODULE, # _In_opt_ hModule
wintypes.LPCWSTR, # _In_ lpName
wintypes.LPCWSTR) # _In_ lpType

kernel32.LoadResource.errcheck = _check_zero
kernel32.LoadResource.restype = wintypes.HGLOBAL
kernel32.LoadResource.argtypes = (
wintypes.HMODULE, # _In_opt_ hModule
wintypes.HRSRC)   # _In_ hResInfo

kernel32.SizeofResource.errcheck = _check_zero
kernel32.SizeofResource.restype = wintypes.DWORD
kernel32.SizeofResource.argtypes = (
wintypes.HMODULE, # _In_opt_ hModule
wintypes.HRSRC)   # _In_ hResInfo

kernel32.LockResource.restype = wintypes.LPVOID
kernel32.LockResource.argtypes = (
wintypes.HGLOBAL,) # _In_ hResData

def get_resource(filename, index, rtype):
try:
hLib = win32api.GetModuleHandle(filename)
except win32api.error as e:
if e.winerror != winerror.ERROR_MOD_NOT_FOUND:
raise
hLib = win32api.LoadLibrary(filename)
index = wintypes.LPCWSTR(index) # MAKEINTRESOURCE
rtype = wintypes.LPCWSTR(rtype)
hResInfo = kernel32.FindResourceW(hLib, index, rtype)
hRes = kernel32.LoadResource(hLib, hResInfo)
size = kernel32.SizeofResource(hLib, hResInfo)
addr = kernel32.LockResource(hRes)
return ctypes.string_at(addr, size)

if __name__ == '__main__':
RT_MANIFEST = 24
manifest = get_resource(None, 1, RT_MANIFEST).decode('utf-8')
print(manifest)

The above demo prints the embedded manifest from python.exe. Of
course, it would be a lot simpler to use win32api.LoadResource [1]:

manifest = win32api.LoadResource(None, RT_MANIFEST, 1).decode('utf-8')

[1]: 
http://docs.activestate.com/activepython/3.4/pywin32/win32api__LoadResource_meth.html
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] How can I restrict users from changing ACE?

2016-10-06 Thread eryk sun
On Thu, Oct 6, 2016 at 11:36 AM, Francoi Xavier  wrote:
> I've just started learning about windows ACL and file permissions. The task
> here is to make a file/folder read-only and should get deleted/modified only
> through our client software. I've denied Write, Delete and other permissions
> for the Lookup Name Everyone which generally restricted users from changing
> the files/folders.

Don't forget to also deny FILE_DELETE_CHILD access on the directory.
Otherwise a user with that right can delete files even when the DACL
otherwise denies delete access.

> But the current user who also happens to be the creator/owner of the
> file/folder can delete the ACE which has been added and he can gain complete
> access rights. Is there a way to restrict this behaviour so that users
> cannot change the file access permissions?

In Vista and later (NT 6+) you can deny WRITE_DAC access to the "OWNER
RIGHTS" security principal. The owner of the object won't be able to
modify the permissions.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] python-win32 Digest, Vol 162, Issue 4

2016-09-23 Thread eryk sun
On Fri, Sep 23, 2016 at 2:12 PM, eryk sun <eryk...@gmail.com> wrote:
> Each kernel object type has a GENERIC_MAPPING that maps generic rights
> to sets of standard and object-specific rights. Before doing an
> AccessCheck, generic rights have to be mapped to specific rights via
> MapGenericMask.
>
> For the File type this generic mapping consists of the following values:
>
> FILE_GENERIC_READ
> FILE_GENERIC_WRITE
> FILE_GENERIC_EXECUTE
> FILE_ALL_ACCESS
>
> If you deny GENERIC_WRITE for a File, that's the same as denying the 6
> rights in FILE_GENERIC_WRITE, which includes the standard SYNCHRONIZE
> and READ_CONTROL rights. You need to mask the value to filter out
> rights that shouldn't be denied. Use the constant SPECIFIC_RIGHTS_ALL,
> which is defined as 0x (i.e. the lower 16 bits of an access mask
> are reserved for object-specific rights). For example:

For reference, here's an access mask diagram:

 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+---+---+---+
|G|G|G|G|Resvd|A| StandardRights| SpecificRights|
|R|W|E|A| |S|   |   |
+-+-+---+---+

Generic Read
Generic Write
Generic Execute
Generic All
Reserved: 3
Access SACL
Standard Rights: 8
Specific Rights: 16

The four most significant bits are the generic rights. Before
evaluating an AccessCheck, the system maps generic rights in access
masks to the corresponding standard and specific rights.

Only 5 of the 8 possible standard rights have been assigned:
SYNCHRONIZE (bit 20), WRITE_OWNER, WRITE_DAC, READ_CONTROL, and DELETE
(bit 16).

The File type assigns 9 out of 16 possible specific rights, from
FILE_WRITE_ATTRIBUTES (bit 8) down to FILE_READ_DATA (bit 0). Some
bits have multiple meanings depending on whether the object is a
directory, data file, or named pipe. For example, bit 2 can mean
FILE_ADD_SUBDIRECTORY, FILE_APPEND_DATA, or FILE_CREATE_PIPE_INSTANCE.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] python-win32 Digest, Vol 162, Issue 4

2016-09-23 Thread eryk sun
On Fri, Sep 23, 2016 at 10:39 AM, Goku Balu  wrote:
> On Tue, Sep 20, 2016 at 9:30 PM,  wrote:
>> >
>> > That doesn't seem like a bug to me. GENERIC_WRITE represents several
>> > permissions mashed together, including FILE_WRITE and read control.
>> >
>> > Perhaps try with just FILE_WRITE on its own?
>>
>> For a file or directory, GENERIC_WRITE (0x8000) gets mapped to
>> FILE_GENERIC_WRITE, which includes the following standard rights
>> (upper 16 bits):
>>
>> SYNCHRONIZE  = 0x0010
>> READ_CONTROL = 0x0002
>>
>> and File-object specific rights (lower 16 bits):
>>
>> FILE_WRITE_ATTRIBUTES = 0x0100
>> FILE_WRITE_EA = 0x0010
>> FILE_APPEND_DATA  = 0x0004
>> FILE_WRITE_DATA   = 0x0002
>>
>> The relevant access right that's being denied in this case is SYNCHRONIZE.
>
> So if we deny WRITE then SYNCHRONIZE will be denied which will in-turn
> affect READ. Is there a way to deny WRITE alone without affecting
> file/folder read?

Each kernel object type has a GENERIC_MAPPING that maps generic rights
to sets of standard and object-specific rights. Before doing an
AccessCheck, generic rights have to be mapped to specific rights via
MapGenericMask.

For the File type this generic mapping consists of the following values:

FILE_GENERIC_READ
FILE_GENERIC_WRITE
FILE_GENERIC_EXECUTE
FILE_ALL_ACCESS

If you deny GENERIC_WRITE for a File, that's the same as denying the 6
rights in FILE_GENERIC_WRITE, which includes the standard SYNCHRONIZE
and READ_CONTROL rights. You need to mask the value to filter out
rights that shouldn't be denied. Use the constant SPECIFIC_RIGHTS_ALL,
which is defined as 0x (i.e. the lower 16 bits of an access mask
are reserved for object-specific rights). For example:

import win32security
import ntsecuritycon

WORLD = win32security.CreateWellKnownSid(win32security.WinWorldSid)

FILE_WRITE = (ntsecuritycon.FILE_GENERIC_WRITE &
  ntsecuritycon.SPECIFIC_RIGHTS_ALL)

def deny_write(filename, account=None, ace_flags=0):
sd = win32security.GetFileSecurity(
filename, win32security.DACL_SECURITY_INFORMATION)
sid = WORLD if account is None else (
win32security.LookupAccountName(None, account)[0])
dacl = sd.GetSecurityDescriptorDacl()
dacl.AddAccessDeniedAceEx(
win32security.ACL_REVISION_DS, ace_flags, FILE_WRITE, sid)
sd.SetSecurityDescriptorDacl(1, dacl, 0)
win32security.SetFileSecurity(
filename, win32security.DACL_SECURITY_INFORMATION, sd)

For a directory, generic write access entails the ability to write
attributes and add files and subdirectories. Note that file delete
rights are separately controlled by standard DELETE access and the
specific directory right FILE_DELETE_CHILD, which is the right to
delete files or subdirectories of a directory, even if the user is
otherwise denied or not granted DELETE access.

The ace_flags parameter allows controlling whether the ACE is
inherited by subdirectories and files (CONTAINER_INHERIT_ACE,
OBJECT_INHERIT_ACE) , whether the inheritance flags get propagated
(NO_PROPAGATE_INHERIT_ACE), and whether the ACE applies only for
inheritance (INHERIT_ONLY_ACE).
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] File access entries are incorrectly set

2016-09-20 Thread eryk sun
On Mon, Sep 19, 2016 at 10:21 PM, Christopher Nilsson  wrote:
>
> That doesn't seem like a bug to me. GENERIC_WRITE represents several
> permissions mashed together, including FILE_WRITE and read control.
>
> Perhaps try with just FILE_WRITE on its own?

For a file or directory, GENERIC_WRITE (0x8000) gets mapped to
FILE_GENERIC_WRITE, which includes the following standard rights
(upper 16 bits):

SYNCHRONIZE  = 0x0010
READ_CONTROL = 0x0002

and File-object specific rights (lower 16 bits):

FILE_WRITE_ATTRIBUTES = 0x0100
FILE_WRITE_EA = 0x0010
FILE_APPEND_DATA  = 0x0004
FILE_WRITE_DATA   = 0x0002

The relevant access right that's being denied in this case is SYNCHRONIZE.

For example, SetCurrentDirectory uses a synchronous directory handle,
i.e. it calls NtOpenfile with desired access SYNCHRONIZE |
FILE_TRAVERSE and open option FILE_SYNCHRONOUS_IO_NONALERT. Similarly,
for listing a directory, FindFirstFile uses a synchronous handle with
SYNCHRONIZE | FILE_LIST_DIRECTORY access.

The open option FILE_SYNCHRONOUS_IO_NONALERT is defined as follows:

All operations on the file are performed synchronously.
Waits in the system that synchronize I/O queuing and
completion are not subject to alerts. This flag also
causes the I/O system to maintain the file-position
context. If this flag is set, the SYNCHRONIZE flag must
be set in the DesiredAccess parameter.

For example, when FindFirstFile calls NtQueryDirectoryFile
(FileBothDirectoryInformation) to list the directory, the system call
sees the handle is opened for synchronous access, so it waits to
acquire the File object's Lock before calling the filesystem driver
(e.g. NTFS). Then it waits again to complete the request.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32com.server failure: Is debugging possible?

2016-08-28 Thread eryk sun
On Mon, Aug 29, 2016 at 12:08 AM, Bob Hood  wrote:
> Breaking on that function produced the following stack trace:

I'm glad you found the culprit, but actually the "ModLoad" lines are
just printed by the debugger as DLLs are loaded. The stac[k] trace [1]
attempts to walk the stack frames back based on the current stack and
instruction pointers (e.g. rsp and rip for an x64 ISA). The default is
up to 20 frames if a frame count isn't specified. Another starting
point when diagnosing a crash is `!analyze -v` [2] to get a verbose
summary of the exception, but that doesn't apply in your case.

[1]: https://msdn.microsoft.com/en-us/library/ff551943
[2]: https://msdn.microsoft.com/en-us/library/ff562112
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32com.server failure: Is debugging possible?

2016-08-27 Thread eryk sun
On Sat, Aug 27, 2016 at 2:19 PM, Bob Hood  wrote:
>
> From what I can tell, it's not actually a crash.  It appears to be an exit()
> with a result of 1, so it's not going to be easy to track down.

Break on ntdll!RtlExitUserProcess to print a stack trace.
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] win32com.server failure: Is debugging possible?

2016-08-25 Thread eryk sun
On Fri, Aug 26, 2016 at 12:42 AM, Bob Hood  wrote:
> Any suggestions as to how I could determine the cause of the crash without
> having to uninstall ALL of my software?

Configure a postmortem debugger [1] (e.g. windbg -I). Use gflags to
temporarily enable full page-heap verification for Explorer.

[1]: https://msdn.microsoft.com/en-us/library/ff551063
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] Problem registering COM server at installation time

2016-02-01 Thread eryk sun
On Sun, Jan 31, 2016 at 10:42 PM, Mark Hammond  wrote:
> On 17/01/2016 6:51 AM, Malte Forkel wrote:
>>
>> I'm trying the register a COM server using the install script of a built
>> distribution, created with the  bdist_wininst format of distutils. But
>> the script fails with a message that MSVCR90.dll can't be found.
>> Registering the server after the installer is done works fine. I'm using
>> Python 2.7.11 and pywin32-220 on Windows 7.1 64 bit.
>
> That sounds a little strange - the script should only be executed after
> python27.dll is loaded, which itself relies on msvcr90.dll.
>
> Does it happen to work if you put the installer executable in the same
> directory as python27.dll?

Loading msvcr90.dll requires a SxS activation context. Neither
pythoncom27.dll nor pywintypes27.dll have this assembly embedded as
resource #2. I assume that's because they're normally loaded as
extension modules. In this case Python uses the activation context
that gets saved in its DllMain, which includes the VC90 assembly from
the #2 manifest that's embedded in python27.dll. Are these two DLLs
getting loaded in some other way here? If so you can create a context
at runtime from the manifest that's embedded in python27.dll.

pythoncomloader27.dll can load msvcr90.dll because it has the
following manifest embedded as resource #2:



  

  

  

  
  

  

  

___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


[python-win32] python-win32 is for Python on Windows

2015-12-26 Thread eryk sun
On Sat, Dec 26, 2015 at 10:44 AM, Vernon D. Cole  wrote:
>
> 1) this (Python-win32) is not a group for the discussion of Python (in
> general) on Windows platforms.  It is for discussion of a specific toolkit
> (pywin32)

Actually, that's not right:

python-win32 -- Python on Windows (32-bit and 64-bit)

About python-win32:
All issues related to programming Python on Windows. 32-bit and 64-bit,
pywin32 extensions, COM, you name it.

There was some discussion a bit ago about changing the list name to
"python-windows", because "win32" is a legacy name. (Microsoft changed
the API name to "Windows" a while ago.) The discussion resulted in
changing the list title from "Python on win32" [1]  to "Python on
Windows (32-bit and 64-bit)".

[1]: 
https://web.archive.org/web/20150905161144/https://mail.python.org/mailman/listinfo/python-win32
___
python-win32 mailing list
python-win32@python.org
https://mail.python.org/mailman/listinfo/python-win32


Re: [python-win32] drag files with non-ASCII filenames?

2015-12-16 Thread eryk sun
On Tue, Dec 15, 2015 at 2:27 PM, Tim Roberts  wrote:
>
> The Windows console shell is an 8-bit entity.  That means you only have
> 256 characters available at any given time, similar to they way
> non-Unicode strings work in Python 2.

The input and screen buffers of the console (conhost.exe) are UCS-2,
which has been the case since NT 3.1 was released in 1993. There are
display limits, such as not being able to mix narrow and wide glyphs
and not handling characters composed with multiple codes (such as
UTF-16 surrogate pairs). Regardless of what's displayed, the
wide-character API preserves the underlying UTF-16 text.

That said, handling the input buffer requires special care due to how
it represents characters that aren't mapped by the current keyboard
layout. In this case, the WindowProc of conhost.exe handles a
WM_DROPFILES [1] message as if it's pasted from the clipboard. It
loops over the string to create an INPUT_RECORD [2] array. Each
character is mapped in the current keyboard layout via VkKeyScan [3].
If this fails, the console uses a sequence of Alt+Numpad key event
records. (At the end of this reply I'm including a commented
transcript of a session with a debugger attached to conhost.exe in
Windows 10. I set a breakpoint on s_DoStringPaste to watch how it
handled pasting "À" into the input buffer.)

A client program that calls ReadConsoleW [4] doesn't have to worry
about this. The console internally handles decoding the Alt+Numpad
sequence when it writes the input to the caller's wide-character
buffer. Microsoft's getwch function instead calls ReadConsoleInputW
[5] to be able to read extended keys and avoid discarding non-keyboard
events, but it doesn't handle the Alt+Numpad case. Handling these
sequences requires a custom implementation of kbhit and getwch.

An example that gets this right is the PDCurses [6] library, when
compiled using the wide-character API. Christoph Gohlke has a Python
curses module [7] for Windows that uses PDCurses, but only the Python
3 version is compiled with Unicode support.

If extended key support (e.g. arrow and function keys) and preserving
mouse, window buffer, and focus events doesn't matter, then just
disable the console's line input and echo mode, and call ReadConsoleW
to read a character at a time. This lets the console handle the
Alt+Numpad events for you. Here's example ctypes code for this limited
implementation of kbhit and getwch. It's not broadly tested, so caveat
emptor. I did check that it worked with file drops and pasting Unicode
strings into the console, as well as manual Alt+Numpad input.

import msvcrt
import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

STD_INPUT_HANDLE = -10
KEY_EVENT = 1
VK_MENU = 0x12
ENABLE_LINE_INPUT = 2
ENABLE_ECHO_INPUT = 4

wintypes.CHAR = ctypes.c_char

class INPUT_RECORD(ctypes.Structure):
class EVENT_RECORD(ctypes.Union):
class KEY_EVENT_RECORD(ctypes.Structure):
class UCHAR(ctypes.Union):
_fields_ = (('UnicodeChar', wintypes.WCHAR),
('AsciiChar',   wintypes.CHAR))
_fields_ = (('bKeyDown',  wintypes.BOOL),
('wRepeatCount',  wintypes.WORD),
('wVirtualKeyCode',   wintypes.WORD),
('wVirtualScanCode',  wintypes.WORD),
('uChar', UCHAR),
('dwControlKeyState', wintypes.DWORD))
_fields_ = (('KeyEvent', KEY_EVENT_RECORD),)
_fields_ = (('EventType', wintypes.WORD),
('Event', EVENT_RECORD))

def kbhit():
handle = kernel32.GetStdHandle(STD_INPUT_HANDLE)
npend = wintypes.DWORD()
npeek = wintypes.DWORD()
if (not kernel32.GetNumberOfConsoleInputEvents(
handle, ctypes.byref(npend)) or
npend.value == 0):
return False
inbuf = (INPUT_RECORD * npend.value)()
if (not kernel32.PeekConsoleInputW(
handle, inbuf, npend, ctypes.byref(npeek)) or
npeek.value == 0):
return False
peek = (INPUT_RECORD * npeek.value).from_buffer(inbuf)
for p in peek:
if p.EventType != KEY_EVENT:
continue
e = p.Event.KeyEvent
if (e.bKeyDown or (e.wVirtualKeyCode == VK_MENU and
   e.uChar.UnicodeChar)):
return True
return False

def getwch():
handle = kernel32.GetStdHandle(STD_INPUT_HANDLE)
old_mode = wintypes.DWORD()
if not kernel32.GetConsoleMode(handle, ctypes.byref(old_mode)):
raise ctypes.WinError(ctypes.get_last_error())
mode = old_mode.value & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)
kernel32.SetConsoleMode(handle, mode)
try: