Search code examples
pythonwindowspyinstalleruac

Running python program with admin priviledges without the UAC prompt


I am making a python app and compiled it using pyinstaller. There is no problem when the user installs the app only for himself/herself , but when he/she installs for all users, it gives a permission denied error.

It's basically a chatbot I made for a project. When the program is run the first time, it takes some basic data from the user and stores in a file. There's no problem when the program is installed for that user only,i.e., the program in stored in the user's appdata folder.

But when the user tries to install it for all users, i.e., the program is stored in the program files(x86) folder, it gives a permission error as the folder is system protected.

I tried to bypass it by adding the --uac-admin flag, but it is not convenient to go through the UAC prompt every time the user wants to run the program.

I wanted to know if there was a way to run the program as admin without the UAC prompt.

Any help would be highly appreciated!


Solution

    • you want all users to be able to modify your application in it's Program Files (x86) folder
    • so have your installation program change the permissions on your folder
    • so that Everyone has Full Control to the folder

    If you've decided that anyone should be able to modify your chat program at any time without having to be an administrator: then give them permission.

    Here's some pseudocode that will grant Full Control to all users.

    procedure GrantAllUsersFullControlOfFileOrFolder(Path: string);
    const
        SECURITY_NULL_SID_AUTHORITY:    TSIDIdentifierAuthority = (Value: (0,0,0,0,0,0));  //S-1-0  ntifs.h
        SECURITY_WORLD_SID_AUTHORITY:   TSIDIdentifierAuthority = (Value: (0,0,0,0,0,1));  //S-1-1
        SECURITY_LOCAL_SID_AUTHORITY:   TSIDIdentifierAuthority = (Value: (0,0,0,0,0,2));  //S-1-2
        SECURITY_CREATOR_SID_AUTHORITY: TSIDIdentifierAuthority = (Value: (0,0,0,0,0,3));  //S-1-3
        SECURITY_NT_AUTHORITY:          TSIDIdentifierAuthority = (Value: (0,0,0,0,0,5));  //S-1-5
    
        //Relative identifiers (RIDs)
        SECURITY_NULL_RID  = 0;         //in authority S-1-0
        SECURITY_WORLD_RID = 0;         //in authority S-1-1
        SECURITY_LOCAL_RID = 0;         //in authority S-1-2
        SECURITY_CREATOR_OWNER_RID = 0; //in authority S-1-3
        SECURITY_CREATOR_GROUP_RID = 1; //in authority S-1-3
    
        SECURITY_BUILTIN_DOMAIN_RID = $00000020; // 32 --> S-1-5-32
        DOMAIN_ALIAS_RID_ADMINS     = $00000220; //544 --> S-1-5-32-544
        DOMAIN_ALIAS_RID_USERS      = $00000221; //545 --> S-1-5-32-545 A local group that represents all users in the domain.
    var
        usersSID: PSID;
    //  res: BOOL;
        sd: PSECURITY_DESCRIPTOR;
        oldAcl, newAcl: PACL;
        dw: DWORD;
        ea: EXPLICIT_ACCESS;
    begin
        //Create a well-known SID for the "Users" group (S-1-5-32-545)
        usersSid := StringToSid('S-1-5-32-545'); //well-known "Users" group
        try
            //Get the current DACL. Free SecurityDescriptor with LocalFree
            sd := nil;
            dw := GetNamedSecurityInfo(PChar(Path), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, @oldAcl, nil, {var}sd);
            if (dw <> ERROR_SUCCESS) then
                RaiseLastWin32Error;
            try
                // Initialize an EXPLICIT_ACCESS structure for the new ACE
                ZeroMemory(@ea, SizeOf(EXPLICIT_ACCESS));
                ea.grfAccessPermissions  := GENERIC_READ or GENERIC_WRITE or GENERIC_EXECUTE {or GENERIC_ALL}; //Yes, it's not "really" full control
                ea.grfAccessMode         := GRANT_ACCESS;
                ea.grfInheritance        := SUB_CONTAINERS_AND_OBJECTS_INHERIT;
                ea.Trustee.TrusteeForm   := TRUSTEE_IS_SID;
                ea.Trustee.TrusteeType   := TRUSTEE_IS_GROUP; //TRUSTEE_IS_WELL_KNOWN_GROUP;
                ea.Trustee.ptstrName     := PChar(usersSID);
    
                // Create a new ACL that merges the new ACE into the existing ACL. Use LocalFree to free newAcl
                dw := SetEntriesInAcl(1, @ea, oldAcl, {var}newAcl); //use LocalFree to free newAcl
                if dw <> ERROR_SUCCESS then
                    RaiseLastOSError(dw);
                try
                    //Attach the new ACL as the object's new DACL
                    dw := SetNamedSecurityInfo(PChar(Path), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nil, nil, newAcl, nil);
                    if (dw <> ERROR_SUCCESS) then
                        RaiseLastWin32Error;
                finally
                    LocalFree(HLOCAL(newAcl));
                end;
            finally
                LocalFree(HLOCAL(sd));
            end;
        finally
            LocalFree(HLOCAL(usersSid));
        end;
    end;
    

    It also means that malware can modify your program at any time. But you're OK with that, as you said you want any standard users to be able to modify it your app.