windows sdk编程系列文章 ---- 浅析windows安全策略

http://hi.baidu.com/combojiang/item/fe76e5147f64a621f6625c36

理论:
本篇我们将通过一个例子,来大致了解下windows的安全策略。

实现windows安全性的安全组件和数据库有:
1。安全引用监视器   如图,这是位于核心态windows执行体中的一个组件。它负责:
1)定义访问令牌数据结构来表示一个安全环境。
2)执行对象的安全访问检查
3)管理特权(用户权限)
4) 生成所有的结果安全审计消息。

2。本地安全权威子系统这是一个运行\windows\system32\Lsass.exe文件的用户模式进程。它负责:
    1)本地系统安全策略(比如:允许哪些用户登录到本地机器上,口令策略,授予用户和用户组的特权,以及系统安全审计设置
    2)用户认证
    3)发送安全审计消息到事件日志。

在此进程加载了4个策略相关的dll模块,他们是:
netlogon.dll
ntdsapi.dll
samsrv.dll
lsasrv.dll
这四个模块对应于上图中的Netlogon ,Active Directory , LSA server,SAM server.

Netlogon (网络登陆服务),这是一个windows服务,它建立起与域控制器之间的安全通道,对用户和服务进行身份验证。它将用户的凭然后返回用户据传递给域控制器,的域安全标识符和用户权 限。这通常称为 pass-through 身份验证。

Active Directory (活动目录),包括两个方面:目录和与目录相关的服务。目录是存储各种对象的一个物理上的容器,从静态的角度来理解这活动目录与我们以前所结识的“目录”和“文 件夹”没有本质区别,仅仅是一个对象,是一实体;而目录服务是使目录中所有信息和资源发挥作用的服务,活动目录是一个分布式的目录服务,信息可以分散在多 台不同的计算机上,保证用户能够快速访问,因为多台机上有相同的信息,所以在信息容氏方面具有很强的控制能力,正因如此,不管用户从何处访问或信息处在何 处,都对用户提供统一的视图。域是由一组计算机和与他们相关联的安全组构成的,每个安全组被当作单个实体来管理。活动目录存储了有关该域中的对象的信息,这样的对象包括用户、组和计算机。域用户和组的口令信息和特权也被存储在活动目录中。关于活动目录的介绍祥见 http://windows.chinaitlab.com/domain/37480.html

SAM (安全帐户管理器服务),它负责管理一个数据库,该数据库包含了本地机器上定义的用户名和组。

LSA (本地安全权威服务),它实现了Lsass子系统的绝大部分功能。

SRM使用一个称为令牌的对象来标识一个进程或线程地安全环境。安全环境中包括的信息描述了与该进程或者线程相关联的特权、帐户和组。
lkd> dt _token
nt!_TOKEN
   +0x000 TokenSource      : _TOKEN_SOURCE
   +0x010 TokenId          : _LUID
   +0x018 AuthenticationId : _LUID
   +0x020 ParentTokenId    : _LUID
   +0x028 ExpirationTime   : _LARGE_INTEGER
   +0x030 TokenLock        : Ptr32 _ERESOURCE
   +0x038 AuditPolicy      : _SEP_AUDIT_POLICY
   +0x040 ModifiedId       : _LUID
   +0x048 SessionId        : Uint4B
   +0x04c UserAndGroupCount : Uint4B
   +0x050 RestrictedSidCount : Uint4B
+0x054 PrivilegeCount   : Uint4B
   +0x058 VariableLength   : Uint4B
   +0x05c DynamicCharged   : Uint4B
   +0x060 DynamicAvailable : Uint4B
   +0x064 DefaultOwnerIndex : Uint4B
   +0x068 UserAndGroups    : Ptr32 _SID_AND_ATTRIBUTES
   +0x06c RestrictedSids   : Ptr32 _SID_AND_ATTRIBUTES
   +0x070 PrimaryGroup     : Ptr32 Void
   +0x074 Privileges       : Ptr32 _LUID_AND_ATTRIBUTES
   +0x078 DynamicPart      : Ptr32 Uint4B
   +0x07c DefaultDacl      : Ptr32 _ACL
   +0x080 TokenType        : _TOKEN_TYPE
   +0x084 ImpersonationLevel : _SECURITY_IMPERSONATION_LEVEL
   +0x088 TokenFlags       : Uint4B
   +0x08c TokenInUse       : UChar
   +0x090 ProxyData        : Ptr32 _SECURITY_TOKEN_PROXY_DATA
   +0x094 AuditData        : Ptr32 _SECURITY_TOKEN_AUDIT_DATA
   +0x098 OriginatingLogonSession : _LUID
   +0x0a0 VariablePart     : Uint4B

SRM运行在内核模式下,而Lsass运行在用户模式下,他们利用LPC进行通讯。在系统初始化过程中,SRM创建了一个名为SeRmCommandPort的端口,Lsass连接到此端口上。当Lsass进程启动的时候,它创建一个名为SeLsaCommandPort的LPC端口。SRM连接到此端口上,从而建立起两者之间的私有通讯。
SRM创建了一个共享内存区,用于传递超过256字节长的消息,他在连接调用中把该共享内存区的句柄传递过去。在系统初始化过程中,一旦SRM与Lsass相互连接起来,他们就不再监听各自的连接端口,因此,后来的用户进程无法连接到这些端口,从而避免一些恶意的破坏。


3. 在SDK中,我们使用下面的三个函数来打开令牌,修改权限。
OpenProcessToken,LookupPrivilegeValue,AdjustTokenPrivileges。
每一个进程,线程都有自己的令牌,线程的令牌通常跟所属进程一样。
前面我们讲过 SRM使用一个称为令牌的对象来标识一个进程或线程地安全环境,令牌中的Privileges决定该令牌的线程或进程可以做哪些事情。 如果想让一个进程有更大的权限去做一些事情,我们就可以通过给进程提权的方式来做到。

步骤如下:

1)首先要获得进程访问令牌的句柄,这可以通过OpenProcessToken得到,函数的原型如下:
BOOL    OpenProcessToken(
          HANDLE    ProcessHandle,    //要修改访问权限的进程句柄
          DWORD    DesiredAccess,        //指定你要进行的操作类型
          PHANDLE    TokenHandle       //返回的访问令牌指针
    );
通过这个函数我们就可以得到当前进程的访问令牌的句柄(指定函数的第一个参数为GetCurrentProcess()就可以了)。

2)获取权限对应的LUID
根据权限结构体,如下所示:
lkd> dt _LUID_AND_ATTRIBUTES
ntdll!_LUID_AND_ATTRIBUTES
   +0x000 Luid             : _LUID
   +0x008 Attributes       : Uint4B

从上面令牌对象的结构,我们看到。
+0x074 Privileges       : Ptr32 _LUID_AND_ATTRIBUTES   表示一个权限数组。
+0x054 PrivilegeCount   : Uint4B                                        表示了权限数组元素的个数。

对应于SDK中的定义是这样的。
typedef struct _TOKEN_PRIVILEGES {
    DWORD PrivilegeCount;
    LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;

这里把令牌对象中关于权限的两个成员PrivilegeCount和 Privileges合并到了一个结构体TOKEN_PRIVILEGES中。
要对一个任意进程(包括系统安全进程和服务进程)进行指定了写相关的访问权的OpenProcess操作,只要当前进程具有SeDeDebug权限就可以 了。要是一个用户是Administrator或是被给予了相应的权限,就可以具有该权限。可是,就算我们用Administrator帐号对一个系统安 全进程执行OpenProcess(PROCESS_ALL_ACCESS,FALSE,       dwProcessID)还是会遇到“访问拒绝”的错误。什么原因呢?原来在默认的情况下进程的一些访问权限是没有被使能(Enabled)的,所以我们 要做的首先是使能这些权限。例如:我们要给当前进程具有SeDeDebug权限,只要找到Privileges 数组中对应的SeDeDebug权限的那个元素,然后让它使能就可以了。Privileges 数组中的每个元素对应一个结构体如下
typedef struct _LUID_AND_ATTRIBUTES {
    LUID Luid;
    DWORD Attributes;
    } LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;

在windows中权限不是用名称来标识的,而是使用一个LUID来标识的。LUID就是指locally unique identifier,我想GUID大家是比较熟悉的,和GUID的要求保证全局唯一不同,LUID只要保证局部唯一,就是指在系统的每一次运行期间保证 是唯一的就可以了。另外和GUID相同的一点,LUID也是一个64位的值,相信大家都看过GUID那一大串的值,我们要怎么样才能知道一个权限对应的 LUID值是多少呢?这就要用到另外一个API函数LookupPrivilegevalue,其原形如下:
BOOL LookupPrivilegevalue(
LPCTSTR lpSystemName, // system name
LPCTSTR lpName, // privilege name
PLUID lpLuid // locally unique identifier
);
第一个参数是系统的名称,如果是本地系统只要指明为NULL就可以了,第三个参数就是返回LUID的指针,第二个参数就是指明了权限的名称,如“SeDebugPrivilege”。


3) 修改权限,即:将2步骤找到的权限项使能。调用AdjustTokenPrivileges对这个访问令牌进行修改。
AdjustTokenPrivileges的原型如下:
BOOL AdjustTokenPrivileges(
HANDLE TokenHandle, // handle to token
BOOL DisableAllPrivileges, // disabling option
PTOKEN_PRIVILEGES NewState, // privilege information
DWORD BufferLength, // size of buffer
PTOKEN_PRIVILEGES PreviousState, // original state buffer
PDWORD ReturnLength // required buffer size
);
第一个参数是访问令牌的句柄;第二个参数决定是进行权限修改还是Disable所有权限;第三个参数指明要修改的权限,是一个指向 TOKEN_PRIVILEGES结构的指针,该结构包含一个数组,数据组的每个项指明了权限的类型和要进行的操作; 第四个参数是结构PreviousState的长度,如果PreviousState为空,该参数应为NULL;第五个参数也是一个指向 TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息,可空;最后一个参数为实际PreviousState结构返回的大小。

ring3下的这几个函数的访问流程如下:
1)OpenProcessToken -〉NtOpenProcessToken ->NtOpenProcessTokenEx->ObOpenObjectByPointer 得到进程的Taken对象

2)LookupPrivilegeValueA-〉LookupPrivilegeValueW-〉LsaOpenPolicy-〉LsaLookupPrivilegeValue-〉NdrClientCall2 -〉通过LPC与SRM通讯,返回LUID。。。。

3)AdjustTokenPrivileges-〉NtAdjustPrivilegesToken-〉修改Taken对象中相关的值。。。

代码:见光盘test ,直接用了鸡蛋壳转载的代码,相关链接http://bbs.pediy.com/showthread.php?t=70540

#include <Windows.h>
#include <Ntsecapi.h>
#include <Aclapi.h>
#include <tlhelp32.h>
#pragma comment (lib,"Kernel32.lib")
#pragma comment (lib,"Advapi32.lib")
#pragma comment(linker, "/ENTRY:main")

//------------------ 数据类型声明开始 --------------------//
typedef struct _PROCESS_BASIC_INFORMATION {
     NTSTATUS ExitStatus;
     ULONG PebBaseAddress;
     ULONG_PTR AffinityMask;
     LONG BasePriority;
     ULONG_PTR UniqueProcessId;
     ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;

typedef PROCESS_BASIC_INFORMATION *PPROCESS_BASIC_INFORMATION;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
     ULONG             ProcessId;
     UCHAR             ObjectTypeNumber;
     UCHAR             Flags;
     USHORT             Handle;
     PVOID             Object;
     ACCESS_MASK         GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _SYSTEM_MODULE_INFORMATION
{
    ULONG Reserved[2];
    PVOID Base;
    ULONG Size;
    ULONG Flags;
    USHORT Index;
    USHORT Unknown;
    USHORT LoadCount;
    USHORT ModuleNameOffset;
    CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;

typedef struct _OBJECT_ATTRIBUTES
{
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef enum _SECTION_INHERIT
{
    ViewShare = 1,
    ViewUnmap = 2
} SECTION_INHERIT;

typedef struct _MY_PROCESS_INFO
{
    ULONG PID;
    ULONG KPEB;
    ULONG CR3;
    CHAR Name[16];
    ULONG Reserved;
} MY_PROCESS_INFO, *PMY_PROCESS_INFO;

typedef struct _CLIENT_ID
{
     HANDLE UniqueProcess;
     HANDLE UniqueThread;
} CLIENT_ID;
typedef CLIENT_ID *PCLIENT_ID;

typedef long NTSTATUS;

//------------------ 数据类型声明结束 --------------------//

//--------------------- 预定义开始 -----------------------//
#define NT_SUCCESS(Status)         ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS              0x00000000
#define STATUS_UNSUCCESSFUL         0xC0000001
#define STATUS_NOT_IMPLEMENTED      0xC0000002
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define STATUS_INVALID_PARAMETER    0xC000000D
#define STATUS_ACCESS_DENIED        0xC0000022
#define STATUS_BUFFER_TOO_SMALL     0xC0000023
#define OBJ_KERNEL_HANDLE           0x00000200
#define SystemModuleInformation     11
#define SystemHandleInformation     0x10

#define InitializeObjectAttributes( p, n, a, r, s ) { (p)->Length = sizeof( OBJECT_ATTRIBUTES );(p)->RootDirectory = r;                 (p)->Attributes = a;                     (p)->ObjectName = n;                       (p)->SecurityDescriptor = s;                 (p)->SecurityQualityOfService = NULL;         }
//--------------------- 预定义结束 -----------------------//

//------------------ Native API声明开始 ------------------//

typedef DWORD (_stdcall *XXXZwQuerySystemInformation)(
                ULONG SystemInformationClass,
                PVOID SystemInformation,
                ULONG SystemInformationLength,
                PULONG ReturnLength
                );

typedef DWORD (_stdcall *XXXZwOpenProcess)(
              OUT PHANDLE             ProcessHandle,
              IN ACCESS_MASK           AccessMask,
              IN POBJECT_ATTRIBUTES   ObjectAttributes,
              IN PCLIENT_ID           ClientId
              );

typedef DWORD (_stdcall *XXXZwAllocateVirtualMemory)(
               IN HANDLE               ProcessHandle,
               IN OUT PVOID             *BaseAddress,
               IN ULONG                 ZeroBits,
               IN OUT PULONG           RegionSize,
               IN ULONG                 AllocationType,
               IN ULONG                 Protect
               );

typedef DWORD (_stdcall *XXXZwDuplicateObject)(
               IN HANDLE               SourceProcessHandle,
               IN PHANDLE               SourceHandle,
               IN HANDLE               TargetProcessHandle,
               OUT PHANDLE             TargetHandle,
               IN ACCESS_MASK           DesiredAccess OPTIONAL,
               IN BOOLEAN               InheritHandle,
               IN ULONG                 Options
               );

typedef DWORD (_stdcall *XXXZwQueryInformationProcess)(
                 IN HANDLE               ProcessHandle,
                 IN PVOID          ProcessInformationClass,
                 OUT PVOID               ProcessInformation,
                 IN ULONG                 ProcessInformationLength,
                 OUT PULONG               ReturnLength
                 );

typedef DWORD (_stdcall *XXXZwProtectVirtualMemory)(
            
              IN HANDLE               ProcessHandle,
              IN OUT PVOID             *BaseAddress,
              IN OUT PULONG           NumberOfBytesToProtect,
              IN ULONG                 NewAccessProtection,
              OUT PULONG               OldAccessProtection
              );

typedef DWORD (_stdcall *XXXZwWriteVirtualMemory)(
               IN HANDLE               ProcessHandle,
               IN PVOID                 BaseAddress,
               IN PVOID                 Buffer,
               IN ULONG                 NumberOfBytesToWrite,
               OUT PULONG               NumberOfBytesWritten OPTIONAL
               );

typedef DWORD (_stdcall *XXXZwClose)(
           IN HANDLE               ObjectHandle
           );

typedef DWORD (_stdcall *XXXZwFreeVirtualMemory)(
            
              IN HANDLE               ProcessHandle,
              IN PVOID                 *BaseAddress,
              IN OUT PULONG           RegionSize,
              IN ULONG                 FreeType
              );

//------------------ Native API声明结束 ------------------//

//------------------ 程序正式开始 ------------------//

DWORD GetPidByName(char *szName)
{
    HANDLE hProcessSnap = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe32={0};
    DWORD dwRet=0;

    hProcessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)return 0;

    pe32.dwSize = sizeof(PROCESSENTRY32);
    if(Process32First(hProcessSnap, &pe32))
    {
       do
       {
            if(lstrcmpi(szName,pe32.szExeFile)==0)
            {
                dwRet=pe32.th32ProcessID;
                break;
            }
       }while (Process32Next(hProcessSnap,&pe32));
    }
    else
        return 0;

    if(hProcessSnap !=INVALID_HANDLE_VALUE)
        CloseHandle(hProcessSnap);
       
    return dwRet;
}

void KillIce(ULONG dwProcessId)
{
    HMODULE hNTDLL = LoadLibrary ("ntdll.dll");
    HANDLE      ph, h_dup;
    ULONG      bytesIO;
    PVOID      buf;
    ULONG         i;
    CLIENT_ID     cid1;
    OBJECT_ATTRIBUTES     attr;
    HANDLE         csrss_id;
    //   HANDLE     SnapShotHandle;
    PROCESS_BASIC_INFORMATION     pbi;
    PVOID         p0, p1;
    ULONG         sz, oldp;
    ULONG         NumOfHandle;
    PSYSTEM_HANDLE_INFORMATION     h_info;

    csrss_id = (HANDLE)GetPidByName("csrss.exe");
    attr.Length = sizeof(OBJECT_ATTRIBUTES);
    attr.RootDirectory = 0;
    attr.ObjectName = 0;
    attr.Attributes = 0;
    attr.SecurityDescriptor = 0;
    attr.SecurityQualityOfService = 0;

    cid1.UniqueProcess = csrss_id;
    cid1.UniqueThread = 0;
    XXXZwOpenProcess ZwOpenProcess;
    ZwOpenProcess = (XXXZwOpenProcess)GetProcAddress( GetModuleHandle("ntdll.dll"), "ZwOpenProcess");
    ZwOpenProcess(&ph, PROCESS_ALL_ACCESS, &attr, &cid1);

    bytesIO = 0x400000;
    buf = 0;
    XXXZwAllocateVirtualMemory ZwAllocateVirtualMemory;
    ZwAllocateVirtualMemory = (XXXZwAllocateVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwAllocateVirtualMemory");
    ZwAllocateVirtualMemory(GetCurrentProcess(), &buf, 0, &bytesIO, MEM_COMMIT, PAGE_READWRITE);

    XXXZwQuerySystemInformation ZwQuerySystemInformation;
    ZwQuerySystemInformation = (XXXZwQuerySystemInformation)GetProcAddress( GetModuleHandle("ntdll.dll"), "ZwQuerySystemInformation");
    ZwQuerySystemInformation(SystemHandleInformation, buf, 0x400000, &bytesIO);
    NumOfHandle = (ULONG)buf;
    h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);

    for (i= 0 ; i<NumOfHandle; i++, h_info++)
    {  
       if ((h_info->ProcessId == (ULONG)csrss_id)&&(h_info->ObjectTypeNumber == 5))
       {
            XXXZwDuplicateObject ZwDuplicateObject;
            ZwDuplicateObject = (XXXZwDuplicateObject)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwDuplicateObject");
            XXXZwQueryInformationProcess ZwQueryInformationProcess;
            ZwQueryInformationProcess = (XXXZwQueryInformationProcess)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwQueryInformationProcess");
            if (ZwDuplicateObject(ph, (PHANDLE)h_info->Handle, (HANDLE)-1, &h_dup,
                                       0, 0, DUPLICATE_SAME_ACCESS) == STATUS_SUCCESS)
                 ZwQueryInformationProcess(h_dup, 0, &pbi, sizeof(pbi), &bytesIO);
            if (pbi.UniqueProcessId == dwProcessId)
            {
                MessageBox(0, "目标已确定!", "OK", MB_OK);
                XXXZwProtectVirtualMemory ZwProtectVirtualMemory;
                ZwProtectVirtualMemory = (XXXZwProtectVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwProtectVirtualMemory");
                XXXZwWriteVirtualMemory ZwWriteVirtualMemory;
                ZwWriteVirtualMemory = (XXXZwWriteVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwWriteVirtualMemory");
                XXXZwClose ZwClose;
                ZwClose = (XXXZwClose)GetProcAddress(GetModuleHandle("ZwClose"), "ZwClose");
                for (i = 0x1000; i<0x80000000; i = i + 0x1000)
                {
                    p0 = (PVOID)i;
                    p1 = p0;
                    sz = 0x1000;
                    if (ZwProtectVirtualMemory(h_dup, &p1, &sz, PAGE_EXECUTE_READWRITE, &oldp) == STATUS_SUCCESS)
                    {             
                           ZwWriteVirtualMemory(h_dup, p0, buf, 0x1000, &oldp);
                    }         
                }
                MessageBox(0, "任务已完成!","OK", 0);
                ZwClose(h_dup);    
                break;
            }
       }
    }

    bytesIO = 0;
    XXXZwFreeVirtualMemory ZwFreeVirtualMemory;
    ZwFreeVirtualMemory = (XXXZwFreeVirtualMemory)GetProcAddress(GetModuleHandle("ntdll.dll"),   "ZwFreeVirtualMemory");
    ZwFreeVirtualMemory(GetCurrentProcess(), &buf, &bytesIO, MEM_RELEASE);

    FreeLibrary(hNTDLL);  

}

BOOL EnablePrivilege(HANDLE hToken,LPCTSTR szPrivName,BOOL fEnable)
{
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    LookupPrivilegeValue(NULL,szPrivName,&tp.Privileges[0].Luid);

    tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED:0;
    AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
    return((GetLastError() == ERROR_SUCCESS));
}

void main()
{   
     ULONG Pid;
     HANDLE hToken;
     OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
     EnablePrivilege(hToken,SE_DEBUG_NAME,TRUE);
     if (Pid = GetPidByName("calc.exe"))
     {
         KillIce(Pid);
     }    
     ExitProcess(0);
}

分析: 以下是windows NT中定义的权限:
#define SE_CREATE_TOKEN_NAME              TEXT("SeCreateTokenPrivilege")
#define SE_ASSIGNPRIMARYTOKEN_NAME        TEXT("SeAssignPrimaryTokenPrivilege")
#define SE_LOCK_MEMORY_NAME               TEXT("SeLockMemoryPrivilege")
#define SE_INCREASE_QUOTA_NAME            TEXT("SeIncreaseQuotaPrivilege")
#define SE_UNSOLICITED_INPUT_NAME         TEXT("SeUnsolicitedInputPrivilege")
#define SE_MACHINE_ACCOUNT_NAME           TEXT("SeMachineAccountPrivilege")
#define SE_TCB_NAME                       TEXT("SeTcbPrivilege")
#define SE_SECURITY_NAME                  TEXT("SeSecurityPrivilege")
#define SE_TAKE_OWNERSHIP_NAME            TEXT("SeTakeOwnershipPrivilege")
#define SE_LOAD_DRIVER_NAME               TEXT("SeLoadDriverPrivilege")
#define SE_SYSTEM_PROFILE_NAME            TEXT("SeSystemProfilePrivilege")
#define SE_SYSTEMTIME_NAME                TEXT("SeSystemtimePrivilege")
#define SE_PROF_SINGLE_PROCESS_NAME       TEXT("SeProfileSingleProcessPrivilege")
#define SE_INC_BASE_PRIORITY_NAME         TEXT("SeIncreaseBasePriorityPrivilege")
#define SE_CREATE_PAGEFILE_NAME           TEXT("SeCreatePagefilePrivilege")
#define SE_CREATE_PERMANENT_NAME          TEXT("SeCreatePermanentPrivilege")
#define SE_BACKUP_NAME                    TEXT("SeBackupPrivilege")
#define SE_RESTORE_NAME                   TEXT("SeRestorePrivilege")
#define SE_SHUTDOWN_NAME                  TEXT("SeShutdownPrivilege")
#define SE_DEBUG_NAME                     TEXT("SeDebugPrivilege")
#define SE_AUDIT_NAME                     TEXT("SeAuditPrivilege")
#define SE_SYSTEM_ENVIRONMENT_NAME        TEXT("SeSystemEnvironmentPrivilege")
#define SE_CHANGE_NOTIFY_NAME             TEXT("SeChangeNotifyPrivilege")
#define SE_REMOTE_SHUTDOWN_NAME           TEXT("SeRemoteShutdownPrivilege")
#define SE_UNDOCK_NAME                    TEXT("SeUndockPrivilege")
#define SE_SYNC_AGENT_NAME                TEXT("SeSyncAgentPrivilege")
#define SE_ENABLE_DELEGATION_NAME         TEXT("SeEnableDelegationPrivilege")
#define SE_MANAGE_VOLUME_NAME             TEXT("SeManageVolumePrivilege")
#define SE_IMPERSONATE_NAME               TEXT("SeImpersonatePrivilege")
#define SE_CREATE_GLOBAL_NAME             TEXT("SeCreateGlobalPrivilege")

其中的SE_DEBUG_NAME 权限是这样描述的,如果调用者有这个特权的话,则进程管理器允许通过NtOpenProcess访问任何一个进程,而不考虑该进程的安全描述符是否允许。
代码如下:
     OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);
     EnablePrivilege(hToken,SE_DEBUG_NAME,TRUE);

其中:EnablePrivilege函数实现了提权。
BOOL EnablePrivilege(HANDLE hToken,LPCTSTR szPrivName,BOOL fEnable)
{
    TOKEN_PRIVILEGES tp;
    //设置特权数组的元素个数
    tp.PrivilegeCount = 1;

    // 取得szPrivName对应的[LUID](本地唯一的标识符)值
    LookupPrivilegeValue(NULL,szPrivName,&tp.Privileges[0].Luid);

    //设置[LUID]的属性值
    tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED:0;

    // 为当前进程取得关闭系统的特权
    AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
    return((GetLastError() == ERROR_SUCCESS));
}


由于本例的作用是采用内存清0的方式关闭目标进程的。所以当前进程需要先有这个权限才行。
下面GetPidByName函数的作用是根据进程文件名,获取进程的ID。这个ID用于后续打开的进程。

DWORD GetPidByName(char *szName)
{
    HANDLE hProcessSnap = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe32={0};
    DWORD dwRet=0;

    hProcessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(hProcessSnap == INVALID_HANDLE_VALUE)return 0;

    pe32.dwSize = sizeof(PROCESSENTRY32);
    if(Process32First(hProcessSnap, &pe32))
    {
       do
       {
            if(lstrcmpi(szName,pe32.szExeFile)==0)
            {
                dwRet=pe32.th32ProcessID;
                break;
            }
       }while (Process32Next(hProcessSnap,&pe32));
    }
    else
        return 0;

    if(hProcessSnap !=INVALID_HANDLE_VALUE)
        CloseHandle(hProcessSnap);
       
    return dwRet;
}

这里用到了3个关键的函数:CreateToolhelp32Snapshot(),Process32First()和Process32Next()。下面给出了关于这三个函数的原形和参数说明;

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, //系统快照要查看的信息类型
DWORD th32ProcessID      //值0表示当前进程
);
BOOL WINAPI Process32First(
HANDLE hSnapshot,        //CreateToolhelp32Snapshot()创建的快照句柄
LPPROCESSENTRY32 lppe //指向进程入口结构
);
BOOL WINAPI Process32Next(
HANDLE hSnapshot,        //这里参数同Process32First
LPPROCESSENTRY32 lppe //同上
);

首先使用CreateToolhelp32Snapshot()创建系统快照句柄(hProcessSnap 是我们声明用来保存创建的快照句柄)
hProcessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
然后调用Process32First()获得系统快照中的第一个进程信息。
if(Process32First(hProcessSnap, &pe32))
接着用一个循环调用来遍历系统中所有运行的进程。一旦找到就返回进程的ID。
           if(lstrcmpi(szName,pe32.szExeFile)==0)
            {
                dwRet=pe32.th32ProcessID;
                break;
            }


后面的代码工作原理是:
首先了解下内核对象及句柄
内核对象是由系统分配的一块内存,必须有系统来维护和访问。windows 系统内核的对象管理器,管理着所有的内核对象,象进程、线程、文件、文件映射、事件、互斥量、信号量、管道、邮槽等都是内核对象。
内核对象是系统级的,独立于进程。也就是说,任何一个进程创建了一个内核对象,理论上讲,其他进程都应该能访问,只要进程获得该内核对象的句柄。事实上,NT就是通过对句柄的种种 限制,来达到安全的目的。 在windows系统中,每一个进程都维护一内核对象句柄表。对象句柄是一个索引,指向与进程相关的句柄表中的表项。一个进程的句柄表包含了所有已被进程打开的那些对象的指针。

我们可以使用ZwQuerySystemInformation函数获取所有核心句柄表,这个句柄表,包含了所有的内核对象的句柄。从遍历出来的结果看,它并不能遍历出所有的进程来。我们通过如下代码,测试输出结果。
由于进程的内核对象类型是5,因此我们可以用这样的过滤条件,打印出所有遍历到的进程信息。

      char bufjiang[100] = {0};
   wsprintf(bufjiang,"ProcessID = %d,ObjectType = %d, handle = %#x, object = %#x\n",
            h_info->ProcessId,h_info->ObjectTypeNumber,h_info->Handle,h_info->Object );
        if(h_info->ObjectTypeNumber == 5)
            OutputDebugString(bufjiang);

所以折中的办法就是,由于csrss.exe是Win32子系统进程,在CSRSS.EXE 进程中保留有一份完整的系统所有进程/线程句柄,因此,我们可以通过csrss.exe中的句柄表,来找出所有运行的进程的句柄来。

于是需要打开CSRSS.EXE 进程,获取其进程ID,
csrss_id = (HANDLE)GetPidByName("csrss.exe");
......
ZwOpenProcess(&ph, PROCESS_ALL_ACCESS, &attr, &cid1);

然后,根据下面的判断过滤内核对象类型是5过滤的情况。

if ((h_info->ProcessId == (ULONG)csrss_id)&&(h_info->ObjectTypeNumber == 5))
       {

然后从CSRSS.EXE 进程中的系统进程/线程中使用ZwDuplicateObject复制出一份找到的进程句柄,
if (ZwDuplicateObject(ph, (PHANDLE)h_info->Handle, (HANDLE)-1, &h_dup,
                                       0, 0, DUPLICATE_SAME_ACCESS) == STATUS_SUCCESS)

然后,有了进程句柄,我们就可以获取进程的信息。
                 ZwQueryInformationProcess(h_dup, 0, &pbi, sizeof(pbi), &bytesIO);
得到的进程信息,对应如下结构。
typedef struct _PROCESS_BASIC_INFORMATION {
     NTSTATUS ExitStatus;
     ULONG PebBaseAddress;
     ULONG_PTR AffinityMask;
     LONG BasePriority;
     ULONG_PTR UniqueProcessId;
     ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
接下来,我们继续判断这个进程是不是我们要关闭的目标进程。
    if (pbi.UniqueProcessId == dwProcessId)
            {
找到目标进程后,我们就可以用0填充进程的用户内存空间了。
for (i = 0x1000; i<0x80000000; i = i + 0x1000)
                {
                    p0 = (PVOID)i;
                    p1 = p0;
                    sz = 0x1000;
                    if (ZwProtectVirtualMemory(h_dup, &p1, &sz, PAGE_EXECUTE_READWRITE, &oldp) == STATUS_SUCCESS)
                    {             
                           ZwWriteVirtualMemory(h_dup, p0, buf, 0x1000, &oldp);
                    }         
                }

其中 :    ZwProtectVirtualMemory函数的作用是修改内存块的属性为可读可写可执行。
            ZwWriteVirtualMemory函数的作用是写内存块。这里在内存块中写入0。

最后附上:内核对象类型的定义,上面用到的进程对象类型5 就是OB_TYPE_PROCESS。
#define OB_TYPE_TYPE                    1
#define OB_TYPE_DIRECTORY               2
#define OB_TYPE_SYMBOLIC_LINK           3
#define OB_TYPE_TOKEN                   4
#define OB_TYPE_PROCESS                 5
#define OB_TYPE_THREAD                  6
#define OB_TYPE_EVENT                   7
#define OB_TYPE_EVENT_PAIR              8
#define OB_TYPE_MUTANT                  9
#define OB_TYPE_SEMAPHORE               10
#define OB_TYPE_TIMER                   11
#define OB_TYPE_PROFILE                 12
#define OB_TYPE_WINDOW_STATION          13
#define OB_TYPE_DESKTOP                 14
#define OB_TYPE_SECTION                 15
#define OB_TYPE_KEY                     16
#define OB_TYPE_PORT                    17
#define OB_TYPE_ADAPTER                 18
#define OB_TYPE_CONTROLLER              19
#define OB_TYPE_DEVICE                  20
#define OB_TYPE_DRIVER                  21
#define OB_TYPE_IO_COMPLETION           22
#define OB_TYPE_FILE                    23

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值