Windows'ta Dosya İzinleri... Ve Python'un Gücü...

Programlama ve Script dilleri konusunda bilgi paylaşım alanıdır.
Cevapla
Kullanıcı avatarı
TRWE_2012
Zettabyte1
Zettabyte1
Mesajlar: 15148
Kayıt: 25 Eyl 2013, 13:38
cinsiyet: Erkek
Teşekkür etti: 2505 kez
Teşekkür edildi: 5301 kez

Windows'ta Dosya İzinleri... Ve Python'un Gücü...

Mesaj gönderen TRWE_2012 »

Resim
Kod İçeriği : ( win_acl_manager_v2.py )

Kod: Tümünü seç

"""
win_acl_manager.py
Windows File/Directory ACL Manager
Supports: list, modify, reset, lock, force-delete permissions
Backend: pywin32 (win32security) with ctypes fallback
Interface: CLI (argparse) + interactive menu fallback
"""

import os
import sys
import stat
import argparse
import shutil
import ctypes
from pathlib import Path

# ---------------------------------------------------------------------------
# Python venv bootstrapper
# ---------------------------------------------------------------------------
VENV_DIR = os.path.join(os.path.expanduser("~"), "python-toolchain")
_candidates = ["Scripts/python.exe", "bin/python3", "bin/python"]

if os.path.isdir(VENV_DIR) and sys.executable not in [
    os.path.abspath(os.path.join(VENV_DIR, c)) for c in _candidates
]:
    for _c in _candidates:
        _venv_py = os.path.join(VENV_DIR, _c)
        if os.path.isfile(_venv_py):
            os.execv(_venv_py, [_venv_py] + sys.argv)

# ---------------------------------------------------------------------------
# Backend detection
# ---------------------------------------------------------------------------
try:
    import win32security
    import win32api
    import win32con
    import ntsecuritycon as nsc
    BACKEND = "pywin32"
except ImportError:
    BACKEND = "ctypes"

# ---------------------------------------------------------------------------
# Privilege helpers
# ---------------------------------------------------------------------------

def _is_admin() -> bool:
    """Return True if the current process has administrator privileges."""
    try:
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    except Exception:
        return False


def _enable_privilege_pywin32(privilege_name: str) -> bool:
    """Enable a Windows privilege via pywin32."""
    try:
        hToken = win32security.OpenProcessToken(
            win32api.GetCurrentProcess(),
            win32con.TOKEN_ADJUST_PRIVILEGES | win32con.TOKEN_QUERY,
        )
        luid = win32security.LookupPrivilegeValue(None, privilege_name)
        win32security.AdjustTokenPrivileges(
            hToken, False,
            [(luid, win32con.SE_PRIVILEGE_ENABLED)]
        )
        return True
    except Exception:
        return False


def _enable_privilege_ctypes(privilege_name: str) -> bool:
    """Enable a Windows privilege via ctypes."""
    SE_PRIVILEGE_ENABLED = 0x00000002
    TOKEN_ADJUST_PRIVILEGES = 0x0020
    TOKEN_QUERY = 0x0008

    class LUID(ctypes.Structure):
        _fields_ = [("LowPart", ctypes.c_ulong), ("HighPart", ctypes.c_long)]

    class LUID_AND_ATTRIBUTES(ctypes.Structure):
        _fields_ = [("Luid", LUID), ("Attributes", ctypes.c_ulong)]

    class TOKEN_PRIVILEGES(ctypes.Structure):
        _fields_ = [
            ("PrivilegeCount", ctypes.c_ulong),
            ("Privileges", LUID_AND_ATTRIBUTES * 1),
        ]

    advapi32 = ctypes.windll.advapi32
    kernel32 = ctypes.windll.kernel32

    hToken = ctypes.c_void_p()
    hProcess = kernel32.GetCurrentProcess()
    if not advapi32.OpenProcessToken(
        hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ctypes.byref(hToken)
    ):
        return False

    luid = LUID()
    if not advapi32.LookupPrivilegeValueW(None, privilege_name, ctypes.byref(luid)):
        kernel32.CloseHandle(hToken)
        return False

    tp = TOKEN_PRIVILEGES()
    tp.PrivilegeCount = 1
    tp.Privileges[0].Luid = luid
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED

    result = advapi32.AdjustTokenPrivileges(
        hToken, False, ctypes.byref(tp),
        ctypes.sizeof(tp), None, None
    )
    kernel32.CloseHandle(hToken)
    return result != 0


def enable_privilege(privilege_name: str) -> bool:
    if BACKEND == "pywin32":
        return _enable_privilege_pywin32(privilege_name)
    return _enable_privilege_ctypes(privilege_name)


# ---------------------------------------------------------------------------
# ACL list / read
# ---------------------------------------------------------------------------

def _ace_mask_to_str(mask: int) -> str:
    """
    Convert an ACE access mask to a human-readable string.

    Evaluation order is most-specific first: FULL must be checked
    before READ/WRITE/EXECUTE because those bits are all contained
    within FILE_ALL_ACCESS, causing false READ-only matches when
    the account actually holds full control.
    """
    if BACKEND == "pywin32":
        # FILE_ALL_ACCESS on Windows includes all standard + specific +
        # generic rights.  Test it first before decomposing into parts.
        FILE_ALL_ACCESS = 0x001F01FF  # standard definition; nsc value identical
        if (mask & FILE_ALL_ACCESS) == FILE_ALL_ACCESS:
            return "FULL"

        parts = []
        # Ordered from broadest remaining to narrowest
        checks = [
            (nsc.FILE_GENERIC_READ    | nsc.FILE_GENERIC_WRITE |
             nsc.FILE_GENERIC_EXECUTE,                          "READ|WRITE|EXECUTE"),
            (nsc.FILE_GENERIC_READ    | nsc.FILE_GENERIC_WRITE, "READ|WRITE"),
            (nsc.FILE_GENERIC_READ    | nsc.FILE_GENERIC_EXECUTE,"READ|EXECUTE"),
            (nsc.FILE_GENERIC_WRITE   | nsc.FILE_GENERIC_EXECUTE,"WRITE|EXECUTE"),
            (nsc.FILE_GENERIC_READ,                             "READ"),
            (nsc.FILE_GENERIC_WRITE,                            "WRITE"),
            (nsc.FILE_GENERIC_EXECUTE,                          "EXECUTE"),
        ]
        remaining = mask
        for flag, label in checks:
            if (remaining & flag) == flag:
                parts.append(label)
                remaining &= ~flag
                break  # one composite label is enough; remaining bits shown as hex
        if remaining:
            parts.append(hex(remaining))
        return "|".join(parts) if parts else hex(mask)
    else:
        # ctypes mode: interpret generic rights bits
        if mask & 0x10000000:
            return "FULL"
        parts = []
        if mask & 0x80000000:
            parts.append("READ")
        if mask & 0x40000000:
            parts.append("WRITE")
        if mask & 0x20000000:
            parts.append("EXECUTE")
        if mask & 0x001F01FF:
            parts.append("FULL(specific)")
        return "|".join(parts) if parts else hex(mask)


def list_permissions_pywin32(path: str) -> None:
    sd = win32security.GetFileSecurity(
        path,
        win32security.OWNER_SECURITY_INFORMATION |
        win32security.DACL_SECURITY_INFORMATION,
    )
    owner_sid = sd.GetSecurityDescriptorOwner()
    owner_name, owner_domain, _ = win32security.LookupAccountSid(None, owner_sid)
    print(f"\n  Path  : {path}")
    print(f"  Owner : {owner_domain}\\{owner_name}")

    dacl = sd.GetSecurityDescriptorDacl()
    if dacl is None:
        print("  DACL  : (none / full access to everyone)")
        return

    print(f"  ACEs  : {dacl.GetAceCount()}")
    print(f"  {'Account':<40} {'Type':<8} {'Permissions'}")
    print(f"  {'-'*40} {'-'*8} {'-'*30}")

    for i in range(dacl.GetAceCount()):
        ace = dacl.GetAce(i)
        ace_type = ace[0][0]
        ace_mask  = ace[1]
        ace_sid   = ace[2]
        try:
            name, domain, _ = win32security.LookupAccountSid(None, ace_sid)
            account = f"{domain}\\{name}"
        except Exception:
            account = str(ace_sid)
        type_str = "ALLOW" if ace_type == win32con.ACCESS_ALLOWED_ACE_TYPE else "DENY"
        print(f"  {account:<40} {type_str:<8} {_ace_mask_to_str(ace_mask)}")


def list_permissions_ctypes(path: str) -> None:
    """
    Minimal ACL listing via ctypes.
    Falls back to os.stat for basic permission bits when full DACL
    enumeration is not available without pywin32.
    """
    print(f"\n  Path  : {path}")
    print("  [ctypes mode: basic stat permissions shown]")
    s = os.stat(path)
    mode = s.st_mode
    print(f"  Mode  : {stat.filemode(mode)} ({oct(mode)})")
    print("  Note  : Install pywin32 for full ACL/ACE listing.")


def list_permissions(path: str) -> None:
    if BACKEND == "pywin32":
        list_permissions_pywin32(path)
    else:
        list_permissions_ctypes(path)


# ---------------------------------------------------------------------------
# ACL modify
# ---------------------------------------------------------------------------

def _resolve_account(account: str | None) -> str:
    """Return account name; if None, return current user."""
    if account:
        return account
    if BACKEND == "pywin32":
        return win32api.GetUserNameEx(win32con.NameSamCompatible)
    buf = ctypes.create_unicode_buffer(256)
    size = ctypes.c_ulong(256)
    ctypes.windll.advapi32.GetUserNameW(buf, ctypes.byref(size))
    domain_buf = ctypes.create_unicode_buffer(256)
    domain_size = ctypes.c_ulong(256)
    ctypes.windll.advapi32.GetComputerNameW(domain_buf, ctypes.byref(domain_size))
    return f"{domain_buf.value}\\{buf.value}"


def modify_permissions_pywin32(
    path: str,
    account: str | None,
    allow: bool,
    mask: int,
) -> None:
    account_name = _resolve_account(account)
    sid, _, _ = win32security.LookupAccountName(None, account_name)

    sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
    dacl = sd.GetSecurityDescriptorDacl()
    if dacl is None:
        dacl = win32security.ACL()

    if allow:
        dacl.AddAccessAllowedAce(win32security.ACL_REVISION, mask, sid)
    else:
        dacl.AddAccessDeniedAce(win32security.ACL_REVISION, mask, sid)

    sd.SetSecurityDescriptorDacl(True, dacl, False)
    win32security.SetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION, sd)
    action = "ALLOW" if allow else "DENY"
    print(f"  [OK] {action} {_ace_mask_to_str(mask)} -> {account_name} on {path}")


def modify_permissions(
    path: str,
    account: str | None,
    allow: bool,
    mask: int,
) -> None:
    if BACKEND == "pywin32":
        modify_permissions_pywin32(path, account, allow, mask)
    else:
        print("  [WARN] pywin32 not available. Using os.chmod (basic POSIX bits only).")
        current = stat.S_IMODE(os.stat(path).st_mode)
        if allow:
            os.chmod(path, current | stat.S_IRUSR | stat.S_IWUSR)
        else:
            os.chmod(path, current & ~stat.S_IWRITE)
        print(f"  [OK] chmod applied to {path}")


# ---------------------------------------------------------------------------
# ACL reset (restore inheritance)
# ---------------------------------------------------------------------------

def reset_permissions_pywin32(path: str) -> None:
    """Remove explicit ACEs and restore DACL inheritance from parent."""
    sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
    empty_dacl = win32security.ACL()
    sd.SetSecurityDescriptorDacl(True, empty_dacl, False)
    # SE_DACL_PROTECTED = 0x1000 — clear it to allow inheritance
    sd.SetSecurityDescriptorControl(
        win32security.SE_DACL_PROTECTED, 0
    )
    win32security.SetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION, sd)
    print(f"  [OK] DACL reset (inheritance restored) on {path}")


def reset_permissions_ctypes(path: str) -> None:
    print("  [WARN] pywin32 not available. Applying full read/write via os.chmod.")
    os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
    print(f"  [OK] Permissions reset (basic) on {path}")


def reset_permissions(path: str) -> None:
    if BACKEND == "pywin32":
        reset_permissions_pywin32(path)
    else:
        reset_permissions_ctypes(path)


# ---------------------------------------------------------------------------
# Lock (deny all access for Everyone)
# ---------------------------------------------------------------------------

def lock_path_pywin32(path: str) -> None:
    everyone_sid = win32security.CreateWellKnownSid(
        win32security.WinWorldSid, None
    )
    sd = win32security.GetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION)
    dacl = win32security.ACL()
    dacl.AddAccessDeniedAce(
        win32security.ACL_REVISION,
        nsc.FILE_ALL_ACCESS,
        everyone_sid,
    )
    sd.SetSecurityDescriptorDacl(True, dacl, False)
    win32security.SetFileSecurity(path, win32security.DACL_SECURITY_INFORMATION, sd)
    print(f"  [OK] Locked (DENY ALL for Everyone) -> {path}")


def lock_path_ctypes(path: str) -> None:
    print("  [WARN] pywin32 not available. Removing write bits via os.chmod.")
    os.chmod(path, stat.S_IREAD)
    print(f"  [OK] Write access removed on {path}")


def lock_path(path: str) -> None:
    if BACKEND == "pywin32":
        lock_path_pywin32(path)
    else:
        lock_path_ctypes(path)


# ---------------------------------------------------------------------------
# Force delete
# ---------------------------------------------------------------------------

def _on_rm_error(func, path, exc_info):
    """onerror handler: strip read-only flag then retry."""
    os.chmod(path, stat.S_IWRITE)
    func(path)


def force_delete(path: str) -> None:
    """Force-delete a file or directory tree, taking ownership first."""
    enable_privilege("SeRestorePrivilege")
    enable_privilege("SeTakeOwnershipPrivilege")

    target = Path(path)
    if not target.exists():
        print(f"  [WARN] Path does not exist: {path}")
        return

    # Take ownership of all items
    if BACKEND == "pywin32":
        _take_ownership_recursive(path)

    if target.is_file() or target.is_symlink():
        try:
            os.chmod(path, stat.S_IWRITE)
            os.remove(path)
            print(f"  [OK] Deleted file: {path}")
        except Exception as exc:
            print(f"  [FAIL] {exc}")
    else:
        try:
            shutil.rmtree(path, onerror=_on_rm_error)
            print(f"  [OK] Deleted directory tree: {path}")
        except Exception as exc:
            print(f"  [FAIL] {exc}")


def _take_ownership_recursive(path: str) -> None:
    """Take ownership of path (and all children if directory)."""
    try:
        _take_ownership_single(path)
    except Exception:
        pass
    if os.path.isdir(path):
        for root, dirs, files in os.walk(path):
            for name in dirs + files:
                try:
                    _take_ownership_single(os.path.join(root, name))
                except Exception:
                    pass


def _take_ownership_single(path: str) -> None:
    token = win32security.OpenProcessToken(
        win32api.GetCurrentProcess(),
        win32con.TOKEN_QUERY,
    )
    owner_sid = win32security.GetTokenInformation(
        token, win32security.TokenUser
    )[0]
    sd = win32security.SECURITY_DESCRIPTOR()
    sd.SetSecurityDescriptorOwner(owner_sid, False)
    win32security.SetFileSecurity(
        path, win32security.OWNER_SECURITY_INFORMATION, sd
    )


# ---------------------------------------------------------------------------
# Recursive walker
# ---------------------------------------------------------------------------

def _walk(path: str, recursive: bool, action, **kwargs):
    """Apply action to path, and optionally all children."""
    action(path, **kwargs)
    if recursive and os.path.isdir(path):
        for root, dirs, files in os.walk(path):
            for name in dirs + files:
                child = os.path.join(root, name)
                try:
                    action(child, **kwargs)
                except Exception as exc:
                    print(f"  [SKIP] {child}: {exc}")


# ---------------------------------------------------------------------------
# Mask presets
# ---------------------------------------------------------------------------

def _mask_from_preset(preset: str) -> int:
    if BACKEND == "pywin32":
        presets = {
            "read":    nsc.FILE_GENERIC_READ,
            "write":   nsc.FILE_GENERIC_WRITE,
            "execute": nsc.FILE_GENERIC_EXECUTE,
            "full":    nsc.FILE_ALL_ACCESS,
        }
    else:
        presets = {
            "read":    0x80000000,   # GENERIC_READ
            "write":   0x40000000,   # GENERIC_WRITE
            "execute": 0x20000000,   # GENERIC_EXECUTE
            "full":    0x10000000,   # GENERIC_ALL
        }
    return presets.get(preset.lower(), nsc.FILE_ALL_ACCESS if BACKEND == "pywin32" else 0x10000000)


# ---------------------------------------------------------------------------
# Interactive menu
# ---------------------------------------------------------------------------

def _prompt_path() -> str:
    while True:
        p = input("\n  Target path: ").strip().strip('"')
        if os.path.exists(p):
            return p
        print("  [ERR] Path not found. Try again.")


def _prompt_recursive() -> bool:
    ans = input("  Recursive? (y/N): ").strip().lower()
    return ans == "y"


def interactive_menu() -> None:
    print("\n" + "=" * 58)
    print("  Windows ACL Manager")
    print(f"  Backend : {BACKEND}")
    print(f"  Admin   : {'Yes' if _is_admin() else 'No (some ops may fail)'}")
    print("=" * 58)

    menu = [
        ("List permissions",           "list"),
        ("Modify permissions",         "modify"),
        ("Reset permissions",          "reset"),
        ("Lock (deny all)",            "lock"),
        ("Force delete",               "delete"),
        ("Exit",                       "exit"),
    ]

    while True:
        print()
        for i, (label, _) in enumerate(menu, 1):
            print(f"  [{i}] {label}")

        choice = input("\n  Select: ").strip()
        if not choice.isdigit() or not (1 <= int(choice) <= len(menu)):
            print("  [ERR] Invalid selection.")
            continue

        _, action = menu[int(choice) - 1]

        if action == "exit":
            break

        path = _prompt_path()
        recursive = _prompt_recursive() if action != "delete" else False

        if action == "list":
            if recursive and os.path.isdir(path):
                for root, dirs, files in os.walk(path):
                    list_permissions(root)
                    for f in files:
                        list_permissions(os.path.join(root, f))
            else:
                list_permissions(path)

        elif action == "modify":
            account = input("  Account (leave blank for current user): ").strip() or None
            preset = input("  Permission [read/write/execute/full]: ").strip() or "full"
            allow_str = input("  Allow or Deny? (A/d): ").strip().lower()
            allow = allow_str != "d"
            mask = _mask_from_preset(preset)
            _walk(path, recursive, lambda p, **_: modify_permissions(p, account, allow, mask))

        elif action == "reset":
            _walk(path, recursive, lambda p, **_: reset_permissions(p))

        elif action == "lock":
            _walk(path, recursive, lambda p, **_: lock_path(p))

        elif action == "delete":
            confirm = input(f"  CONFIRM delete '{path}'? (yes/NO): ").strip()
            if confirm.lower() == "yes":
                force_delete(path)
            else:
                print("  Cancelled.")


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------

def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="win_acl_manager",
        description="Windows ACL Manager — list/modify/reset/lock/delete",
    )
    sub = parser.add_subparsers(dest="command")

    # list
    p_list = sub.add_parser("list", help="List ACL permissions")
    p_list.add_argument("path")
    p_list.add_argument("-r", "--recursive", action="store_true")

    # modify
    p_mod = sub.add_parser("modify", help="Add ACE to DACL")
    p_mod.add_argument("path")
    p_mod.add_argument("-u", "--account", default=None, help="Account name (default: current user)")
    p_mod.add_argument("-p", "--perm", default="full", choices=["read", "write", "execute", "full"])
    p_mod.add_argument("-d", "--deny", action="store_true", help="Deny instead of allow")
    p_mod.add_argument("-r", "--recursive", action="store_true")

    # reset
    p_reset = sub.add_parser("reset", help="Restore inheritance / reset DACL")
    p_reset.add_argument("path")
    p_reset.add_argument("-r", "--recursive", action="store_true")

    # lock
    p_lock = sub.add_parser("lock", help="Deny all access (Everyone)")
    p_lock.add_argument("path")
    p_lock.add_argument("-r", "--recursive", action="store_true")

    # delete
    p_del = sub.add_parser("delete", help="Force delete (takes ownership first)")
    p_del.add_argument("path")
    p_del.add_argument("--confirm", action="store_true", help="Skip confirmation prompt")

    return parser


def run_cli(args) -> None:
    if args.command == "list":
        if args.recursive and os.path.isdir(args.path):
            for root, dirs, files in os.walk(args.path):
                list_permissions(root)
                for f in files:
                    list_permissions(os.path.join(root, f))
        else:
            list_permissions(args.path)

    elif args.command == "modify":
        mask = _mask_from_preset(args.perm)
        _walk(
            args.path, args.recursive,
            lambda p, **_: modify_permissions(p, args.account, not args.deny, mask),
        )

    elif args.command == "reset":
        _walk(args.path, args.recursive, lambda p, **_: reset_permissions(p))

    elif args.command == "lock":
        _walk(args.path, args.recursive, lambda p, **_: lock_path(p))

    elif args.command == "delete":
        if not args.confirm:
            ans = input(f"  CONFIRM delete '{args.path}'? (yes/NO): ").strip()
            if ans.lower() != "yes":
                print("  Cancelled.")
                return
        force_delete(args.path)


# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------

def main() -> None:
    if not sys.platform.startswith("win"):
        print("[ERR] This script is designed for Windows only.")
        sys.exit(1)

    if not _is_admin():
        print("[WARN] Not running as Administrator. Some operations may fail.")

    enable_privilege("SeRestorePrivilege")
    enable_privilege("SeTakeOwnershipPrivilege")
    enable_privilege("SeSecurityPrivilege")

    parser = build_parser()
    args = parser.parse_args()

    if args.command:
        run_cli(args)
    else:
        interactive_menu()

    input("\nPress ENTER to exit.")


if __name__ == "__main__":
    main()
Güle güle kullanın...

NOT :

Şuradan esinlenildi....

https://windowsforum.com/threads/python ... ns.338678/

Ayrıca yukarıda verilen site, yapay zeka tarafından yönetiliyor.Belki de bu forumlar'da ilk olabilir.
Cevapla

“Programlama ve Script dilleri” sayfasına dön