GCC及VS编写应用程序获取管理员权限

来源:互联网 发布:魔法王座升级数据 编辑:程序博客网 时间:2024/05/21 15:42

转自 http://www.cnblogs.com/bombless/archive/2010/12/29/handling-windows-uac-feature.html

在MinGW或VS 2005 SP1环境下创建一个要求UAC权限的程序。

之前在给U盘挂自制操作系统的那篇教程里,我给写了一个pdev程序用来输出windows下,系统各磁盘的主引导记录。

不过那个程序用起来每次都要记得用管理员权限启动程序,这多少增加了不便。(特别是对带UAC功能的win7\vista来说)。

如果能让程序启动时自动要求提权,那当然更符合windows程序的一般习惯。

我查到了一篇这种做法的教程:
http://www.zu14.cn/2010/05/14/delphi-win32-program-on-windows7-vista-uac-administrator-rights/

也就是需要用到以下这个xml文件(保存成uac.manifest文件,不过还可以保存成任意的.manifest文件也行。

程序清单文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"><security><requestedPrivileges><requestedExecutionLevel level="requireAdministrator"/></requestedPrivileges></security></trustInfo></assembly>下面具体讲

讲在MinGW或者VS2005 SP1的情况下该怎么做。

VS2005下简单,只需要在项目的资源里加这个uac.manifest文件即可。
我在VS2005的“添加资源”一项里没有找到“添加程序清单”,索性我选择了添加xml文件,并保存为uac.manifest,把以上内容复制到文件中,
再编译,就生成了启动时会提示UAC提权的提示框。(这种要求UAC提权的程序不能直接用F5调试运行,在VC里如果要直接看效果可以按Ctrl + F5 直接运行)。

在MinGW下,这个问题要稍复杂那么一点点。

需要在.rc资源文

#include <winuser.h>1 RT_MANIFEST uac.manifest

这include

#define RT_MANIFEST 24

把这段资源脚本文件保存为uac.rc,放到与前面说的uac.manifest相同的目录下,最后需要用MinGW工具中的windres程序将它处理成.res资源文件。
命令如下:

windres --input-format=rc -O coff -i uac.rc -o uac.res

这样就产生了uac.res资源文件,再接下来用gcc将它和程序源文件一起编译就可以了。于是问题解决!
当然如果嫌这样的.rc文件长了,或者不愿意include一个winuser.h文件,可以写成更加简短的版本:

1 24 uac.manifest

效果一样。(在VC中也可以做同样处理,如果你想在资源里添加对程序清单的描述的话。)

写这篇文章参考了以下几个网址,如果需要深入这个问题可以作为参考:
http://www.cnblogs.com/dflying/archive/2007/03/21/683190.html
(Windows Vista for Developers——第四部分:用户帐号控制(User Account Control,UAC))
这篇文章需要详细关注“使用应用程序清单”这一部分。

http://msdn.microsoft.com/en-us/library/aa381043%28v=VS.85%29.aspx
http://msdn.microsoft.com/fr-fr/library/bb773175%28en-us,VS.85%29.aspx

上面2篇MSDN文章中关于RT_MANIFEST的部分值得注意。

总之原理就是,PE文件的资源中有一种类型是RT_MANIFEST,它的内容实际上是一个xml文件,被称为程序清单文件。
换句话说,这个xml格式的.manifest文件文件会被打包进可执行程序文件中。
而程序清单文件中描述了运行程序需要的权限,因此借助这个机制,有UAC管理功能的windows系统会在启动程序时向用户要求提权。

下面把我的pdev程序贴一下,作为一个要求UAC提权的程序的有趣的例子。它可以判断系统中有哪些磁盘,并打印出磁盘的主引导记录。
(注意,这类程序即使在命令行打开,windows仍然会打开一个新的命令行窗口运行程序,并且程序路径是一个完整路径,
因此这时候我们的命令行程序不得不总是在将要推出前用系统的pause命令做暂停,否则用户看到的会是一个一闪而过的窗口。

代码

#include <stdio.h>#include <stdlib.h>#include <windows.h>void ShowError(){        void *pError;        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|            FORMAT_MESSAGE_FROM_SYSTEM|            FORMAT_MESSAGE_IGNORE_INSERTS,            NULL,GetLastError(),            MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),            (LPTSTR)&pError,0,NULL);        MessageBox(0,(PTSTR)pError,0,0);        LocalFree(pError);}void ReadDevice(HANDLE hDeviceHandle,int DriveNumber,FILE *output_file){    BYTE buffer[512];    DWORD nBytes;    int iCount;    if(hDeviceHandle != INVALID_HANDLE_VALUE){        printf("#正在尝试读取物理驱动器%d ...",DriveNumber);        ReadFile(hDeviceHandle,buffer,sizeof buffer,&nBytes,NULL);    }else{        if(GetLastError() == ERROR_FILE_NOT_FOUND && DriveNumber > 0){            printf("#以上%d项就是系统中现有的全部物理驱动器。",DriveNumber);            system("pause");            exit(0);        }        perror("读取设备时发生错误");        puts("请检查您是否正以管理员账户运行此程序,");        printf("或许物理驱动器%d不存在.\n",DriveNumber);        ShowError();        system("pause");        exit(-1);    }    if(nBytes > 0){        printf("\n#读取了%d字节的数据:\n",nBytes);        for(iCount = 0; iCount < sizeof buffer; ++iCount){            if(iCount % 16 == 0){                printf("\n %04X\t|",iCount);            }            printf(" %02X",buffer[iCount]);        }        printf("\n ----\t ");        for(iCount = 0; iCount < 16; ++iCount){            printf(" --");        }        printf("\n\n");        if(output_file != NULL){            fwrite(buffer,sizeof buffer,1,output_file);            printf("已将以上%d字节的数据写入文件。\n",sizeof buffer);        }    }else{        perror("失败。");        ShowError();        system("pause");        exit(-1);    }}int main(int argc,char *argv[]){    HANDLE hDeviceHandle = NULL;    int iCount;    FILE *output_file = NULL;    TCHAR DriveNameBuffer[MAX_PATH] = TEXT("");    PTSTR DriveNamePrefix = TEXT("\\\\.\\PHYSICALDRIVE");    if(argc > 1){//带命令行参数,说明用户在命令行里使用本程序        if(argc == 3 && !strcmp("-o",argv[1])){            printf("#以512字节为单位连续输出到文件%s...\n",argv[2]);            output_file = fopen(argv[2],"wb");            if(output_file == NULL){                perror("无法打开指定的文件");                system("pause");                exit(-1);            }        }else{            printf("Usage:\n"                "%s -o [filename ...]"                "\n将系统中所有的物理驱动器的主引导记录依次写入文件。\n",                    argv[0]);            printf("\n不合法的命令行参数,将退出程序。");            system("pause");            exit(0);        }    }else{//不带命令行参数,很可能用户是直接双击程序运行的。        output_file = NULL;    }    for(iCount = 0; iCount < 256; ++iCount){        wsprintf(DriveNameBuffer,TEXT("%s%d"),DriveNamePrefix,iCount);        hDeviceHandle = CreateFile(            DriveNameBuffer,            GENERIC_READ,FILE_SHARE_READ,            NULL,OPEN_EXISTING,0,0);        ReadDevice(hDeviceHandle,iCount,output_file);        CloseHandle(hDeviceHandle);    }    return 0;}

编译之前我们先用之前的uac.manifest文件和uac.rc文件准备好我们的uac.res资源文件:

windres --input-format=rc -O coff -i uac.rc -o uac.res

之后将uac.res文件和程序一起打包:(当作.o文件链接处理)

gcc -o pdev pdev.c uac.res
阅读全文
0 0
原创粉丝点击