首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第42期->技术专题
期刊号: 类型: 关键词:
MSDN系列(3)--Administrator用户直接获取SYSTEM权限

作者:"scz" <scz@nsfocus.com>
出处:http://www.nsfocus.com
日期:2003-07-02

标题: MSDN系列(3)--Administrator用户直接获取SYSTEM权限

日期: 2003-06-21 21:51
更新:

--------------------------------------------------------------------------

目录:

    ☆ 概述
    ☆ sysproc.c
    ☆ sysproc_now.c
    ☆ 参考资源

--------------------------------------------------------------------------

☆ 概述

翻看[5]的时候,其书中例8.1演示如何以SYSTEM身份启动cmd。以前我为了达到同样
目的,利用at命令,但平日里禁用Task Scheduler Service的,感觉操作很绕弯,不
爽得很。网上有现成的可执行程序直接获取SYSTEM权限,不喜欢没源码的此类型小工
具,从未试用过。

麻雀兄 <sparrow@smth>在水木清华MSDN版上共享了一份Ashot Oganesyan K的源代码,
也是直接获取SYSTEM权限。

一下子有了两份现成的短小精悍型源代码,心痒难耐,决定演练。最后发现还是麻雀
兄给的代码有实用价值。Gary Nebbett的代码对于2K/XP实际上失去了实用价值,这
要感谢rain的指点,具体技术讨论见后。

本文提供了两份完整源代码,sysproc.c、sysproc_now.c,前者演示Gary Nebbett的
办法,后者演示Ashot Oganesyan K的办法。

Ashot Oganesyan K的源代码只支持到2K,不清楚这位达人是否自己更新过,google
居然搜不到所提源代码(再次感谢sparrow共享),没办法,我只好自己增加了对XP和
2003 Server的支持。此外为方便使用,sysproc_now.c自己找出winlogon.exe的PID,
不必在命令行上指定拥有SYSTEM权限的PID了。

☆ sysproc.c

Gary Nebbett的中心思想就是利用ZwCreateToken()自己创建一个SYSTEM令牌(Token),
作为CreateProcessAsUser()第一形参使用。这个想法很好,我也费了好大劲去读令
牌相关的各类技术文档,可是到2K/XP环境中实测时却未达到预定效果。

开始完全按照Gary Nebbett所述编码,2K中ZwCreateToken()失败返回,说当前帐号
没有足够的权限。XP中说当前帐号不能指派为所伪造的SYSTEM令牌的所有者(Owner)。
关于这点参sysproc.c/CreateSystemToken()函数中的注释,从中也可看出XP比2K多
做了一些安全检查。后来统一使用sysproc.c中演示的方式,这下都成了权限不够的
错误提示。

知道调用ZwCreateToken()需要SE_CREATE_TOKEN_NAME权限,因此在程序中Enable了
该权限,当时相关的代码路径上并未提示Enable失败。因此对"权限不够"感到非常困
惑。鉴于我对Privilege理解不足,干脆不管三七二十一先将winnt.h中所列权限全部
Enable了,还是同样的下场。由此我才想到Administrator是否缺省并未被Grant某些
权限,AdjustTokenPrivileges()只能Enable/Disable那些已经Grant的权限,比如前
面所提的SE_DEBUG_NAME权限,并不能Grant权限。正好rain来北京,我们聚了两天,
顺带就此问题请教了rain和flier。rain为sysproc.c增加了一段代码,显示当前帐号
所拥有的权限,参CreateSystemToken()函数。从中发现Administrator果然没有所需
的SE_CREATE_TOKEN_NAME权限。rain告诉我,2K/XP中这种权限需要显式指派,也就
是"本地安全策略/用户权利指派"所做的事,NT是否需要显式指派我未证实过。缺省
情况下,CreateProcessAsUser()所需SE_ASSIGNPRIMARYTOKEN_NAME权限也未被指派
给管理员帐号。这两种权限在"本地安全策略/用户权利指派"中对应:

SE_CREATE_TOKEN_NAME       - Create a token object
SE_ASSIGNPRIMARYTOKEN_NAME - Replace a process level token

在此加深了对AdjustTokenPrivileges()的记忆。开始对MSDN中的描述并无感性认识,
只判断返回值为FALSE,而未判断GetLastError()为ERROR_NOT_ALL_ASSIGNED的情况,
导致当时相关的代码路径上并未提示Enable失败。

为管理员帐号手工增加这两种权限,注销并重新登录后,Gary Nebbett的目的达到了。

可以不利用"本地安全策略/用户权利指派",而是自己编程实现这个步骤([2])。利用
LsaOpenPolicy()、LsaAddAccountRights()即可,AddPrivilege()演示了这个过程。

但问题是无论如何都必须注销/重登录才会生效。这对我来说,事实上已经失去了利
用价值,我需要的是立即(now)获取SYSTEM权限。

可能有人由ZwCreateToken()联想到LogonUser(),关于后者可以参看[3]、[4],那不
是我需要的东西。

下面给出sysproc.c的完整源代码,秉承一贯风格,以不建工程(Solution/Project)
直接命令行编译为原则。注释相当冗长,那是写给我本人看的,对这些API特别不熟,
不写个注释在附近,下次看不懂是很正常的事,谁让该死的Windows API这么多形参。

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7
* cl sysproc.c /Os /G6 /W3
*
* Gary Nebbett
* rain
*/

/************************************************************************
*                                                                      *
*                               Head File                              *
*                                                                      *
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <ntsecapi.h>

/************************************************************************
*                                                                      *
*                               Macro                                  *
*                                                                      *
************************************************************************/

#pragma comment( linker, "/subsystem:console" )
#pragma comment( lib,    "advapi32.lib"       )

typedef LONG NTSTATUS;

/*
* you'll find a list of NTSTATUS status codes in the DDK header
* ntstatus.h (\WINDDK\2600.1106\inc\ddk\wxp\)
*/
#define NT_SUCCESS(status)          ((NTSTATUS)(status)>=0)
#define STATUS_SUCCESS              ((NTSTATUS)0x00000000L)

/*
*************************************************************************
* ntdef.h
*/

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

/*
* ntdef.h
*************************************************************************
*/

/*
* 参看DDK文档以及<<Windows NT/2000 Native API Reference>> - Gary Nebbett
* 这些Native API由ntdll.dll输出
*/
typedef ULONG    ( __stdcall *RTLNTSTATUSTODOSERROR ) ( IN NTSTATUS Status );
typedef NTSTATUS ( __stdcall *ZWCREATETOKEN         ) ( OUT PHANDLE TokenHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN TOKEN_TYPE Type, IN PLUID AuthenticationId, IN PLARGE_INTEGER ExpirationTime, IN PTOKEN_USER User, IN PTOKEN_GROUPS Groups, IN PTOKEN_PRIVILEGES Privileges, IN PTOKEN_OWNER Owner, IN PTOKEN_PRIMARY_GROUP PrimaryGroup, IN PTOKEN_DEFAULT_DACL DefaultDacl, IN PTOKEN_SOURCE Source );

/************************************************************************
*                                                                      *
*                            Function Prototype                        *
*                                                                      *
************************************************************************/

static BOOL   AddCurrentProcessPrivilege
                                ( LPWSTR PrivilegeName );
static BOOL   AddPrivilege      ( LSA_HANDLE PolicyHandle,
                                  PSID AccountSid, LPWSTR PrivilegeName );
static HANDLE CreateSystemToken ( void );
static BOOL   DisableCurrentProcessSomePrivilege
                                ( void );
static BOOL   EnableCurrentProcessSomePrivilege
                                ( void );
static PVOID  GetFromToken      ( HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass );
static BOOL   LocateNtdllEntry  ( void );
static void   PrintWin32Error   ( char *message, DWORD dwMessageId );
static void   PrintZwError      ( char *message, NTSTATUS status );
static BOOL   RemoveCurrentProcessPrivilege
                                ( LPWSTR PrivilegeName );
static BOOL   RemovePrivilege   ( LSA_HANDLE PolicyHandle,
                                  PSID AccountSid, LPWSTR PrivilegeName );
static BOOL   SetCurrentProcessPrivilege
                                ( LPCTSTR PrivilegeName, BOOL EnableFlag );
static BOOL   SetPrivilege      ( HANDLE TokenHandle, LPCTSTR PrivilegeName,
                                  BOOL EnableFlag );

/************************************************************************
*                                                                      *
*                            Static Global Var                         *
*                                                                      *
************************************************************************/

/*
* 由ntdll.dll输出的Native API函数指针
*/
static RTLNTSTATUSTODOSERROR RtlNtStatusToDosError = NULL;
static ZWCREATETOKEN         ZwCreateToken         = NULL;

/************************************************************************/

static BOOL AddCurrentProcessPrivilege ( LPWSTR PrivilegeName )
{
    NTSTATUS              status;
    BOOL                  ret                 = FALSE;
    LSA_HANDLE            PolicyHandle        = NULL;
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE                CurrentProcessToken = NULL;
    PTOKEN_USER           token_user          = NULL;

    ZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
    /*
     * NTSTATUS LsaOpenPolicy
     * (
     *     PLSA_UNICODE_STRING    SystemName,
     *     PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
     *     ACCESS_MASK            DesiredAccess,
     *     PLSA_HANDLE            PolicyHandle
     * );
     */
    status = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &PolicyHandle );
    if ( status != STATUS_SUCCESS )
    {
        PrintWin32Error( "LsaOpenPolicy() failed", LsaNtStatusToWinError( status ) );
        goto AddCurrentProcessPrivilege_exit;
    }
    if ( FALSE == OpenProcessToken( GetCurrentProcess(),
                                    TOKEN_QUERY,
                                    &CurrentProcessToken ) )
    {
        PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
        goto AddCurrentProcessPrivilege_exit;
    }
    if ( NULL == ( token_user = ( PTOKEN_USER )GetFromToken( CurrentProcessToken, TokenUser ) ) )
    {
        goto AddCurrentProcessPrivilege_exit;
    }
    if ( FALSE == AddPrivilege( PolicyHandle,
                                token_user->User.Sid,
                                PrivilegeName ) )
    {
        goto AddCurrentProcessPrivilege_exit;
    }
    ret    = TRUE;

AddCurrentProcessPrivilege_exit:

    if ( NULL != token_user )
    {
        free( token_user );
        token_user = NULL;
    }
    if ( NULL != CurrentProcessToken )
    {
        CloseHandle( CurrentProcessToken );
        CurrentProcessToken = NULL;
    }
    if ( NULL != PolicyHandle )
    {
        LsaClose( PolicyHandle );
        PolicyHandle = NULL;
    }
    return( ret );
}  /* end of AddCurrentProcessPrivilege */

/*
* 留心第二形参是宽字符串
*/
static BOOL AddPrivilege ( LSA_HANDLE PolicyHandle, PSID AccountSid, LPWSTR PrivilegeName )
{
    BOOL               ret = FALSE;
    LSA_UNICODE_STRING UserRights;
    USHORT             StringLength;
    NTSTATUS           status;

    if ( PrivilegeName == NULL )
    {
        goto AddPrivilege_exit;
    }
    StringLength             = wcslen( PrivilegeName );
    UserRights.Buffer        = PrivilegeName;
    UserRights.Length        = StringLength * sizeof( WCHAR );
    UserRights.MaximumLength = ( StringLength + 1 ) * sizeof( WCHAR );
    /*
     * Header : Declared in Ntsecapi.h.
     * Library: Use Advapi32.lib.
     *
     * NTSTATUS LsaAddAccountRights
     * (
     *     LSA_HANDLE          PolicyHandle,
     *     PSID                AccountSid,
     *     PLSA_UNICODE_STRING UserRights,
     *     ULONG               CountOfRights
     * );
     */
    status                   = LsaAddAccountRights( PolicyHandle, AccountSid, &UserRights, 1 );
    if ( status != STATUS_SUCCESS )
    {
        PrintWin32Error( "LsaAddAccountRights() failed", LsaNtStatusToWinError( status ) );
        goto AddPrivilege_exit;
    }
    ret                      = TRUE;

AddPrivilege_exit:

    return( ret );
}  /* end of AddPrivilege */

static HANDLE CreateSystemToken ( void )
{
    NTSTATUS                    status;
    HANDLE                      CurrentProcessToken         = NULL;
    HANDLE                      SystemToken                 = NULL;
    SID_IDENTIFIER_AUTHORITY    sid_identifier_authority    = SECURITY_NT_AUTHORITY;
    PTOKEN_PRIVILEGES           token_privileges            = NULL;
    /*
     * typedef struct _TOKEN_USER
     * {
     *     SID_AND_ATTRIBUTES User;
     * } TOKEN_USER, *PTOKEN_USER;
     *
     * typedef struct _SID_AND_ATTRIBUTES
     * {
     *     PSID  Sid;
     *     DWORD Attributes;
     * } SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES;
     */
    TOKEN_USER                  token_user                  = { { NULL, 0 } };
    /*
     * typedef struct _TOKEN_SOURCE
     * {
     *     Char SourceName[8];
     *     LUID SourceIdentifier;
     * } TOKEN_SOURCE, *PTOKEN_SOURCE;
     *
     * typedef struct _LUID
     * {
     *     DWORD LowPart;
     *     LONG  HighPart;
     * } LUID, *PLUID;
     */
    TOKEN_SOURCE                token_source                =
    {
        { '*', '*', 'A', 'N', 'O', 'N', '*', '*' },
        { 0, 0 }
    };
    /*
     * typedef struct _TOKEN_STATISTICS
     * {
     *     LUID                         TokenId;
     *     LUID                         AuthenticationId;
     *     LARGE_INTEGER                ExpirationTime;
     *     TOKEN_TYPE                   TokenType;
     *     SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
     *     DWORD                        DynamicCharged;
     *     DWORD                        DynamicAvailable;
     *     DWORD                        GroupCount;
     *     DWORD                        PrivilegeCount;
     *     LUID                         ModifiedId;
     * } TOKEN_STATISTICS, *PTOKEN_STATISTICS;
     */
    PTOKEN_STATISTICS           token_statistics            = NULL;
    /*
     * typedef struct _TOKEN_OWNER
     * {
     *     PSID Owner;
     * } TOKEN_OWNER, *PTOKEN_OWNER;
     */
    TOKEN_OWNER                 token_owner                 = { NULL };
    LUID                        AuthenticationId            = SYSTEM_LUID;
    /*
     * typedef struct _SECURITY_QUALITY_OF_SERVICE
     * {
     *     DWORD                          Length;
     *     SECURITY_IMPERSONATION_LEVEL   ImpersonationLevel;
     *     SECURITY_CONTEXT_TRACKING_MODE ContextTrackingMode;
     *     BOOLEAN                        EffectiveOnly;
     * } SECURITY_QUALITY_OF_SERVICE, *PSECURITY_QUALITY_OF_SERVICE;
     */
    SECURITY_QUALITY_OF_SERVICE security_quality_of_service =
    {
        sizeof( security_quality_of_service ),
        SecurityAnonymous,
        // SecurityDelegation,
        SECURITY_STATIC_TRACKING,
        FALSE
    };
    OBJECT_ATTRIBUTES           object_attributes           =
    {
        sizeof( object_attributes ),
        NULL,
        NULL,
        0,
        NULL,
        &security_quality_of_service
    };
    DWORD                       PrivilegeCount;
    char                        PrivilegeName[256];
    char                        PrivilegeDisplayName[256];
    DWORD                       NameSize;
    DWORD                       LanguageId;

    if ( FALSE == OpenProcessToken( GetCurrentProcess(),
                                    TOKEN_QUERY | TOKEN_QUERY_SOURCE,
                                    &CurrentProcessToken ) )
    {
        PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
        goto CreateSystemToken_exit;
    }
    /*
     * BOOL AllocateAndInitializeSid
     * (
     *     PSID_IDENTIFIER_AUTHORITY  pIdentifierAuthority,
     *     BYTE                       nSubAuthorityCount,
     *     DWORD                      dwSubAuthority0,
     *     DWORD                      dwSubAuthority1,
     *     DWORD                      dwSubAuthority2,
     *     DWORD                      dwSubAuthority3,
     *     DWORD                      dwSubAuthority4,
     *     DWORD                      dwSubAuthority5,
     *     DWORD                      dwSubAuthority6,
     *     DWORD                      dwSubAuthority7,
     *     PSID                      *pSid
     * );
     */
    if ( FALSE == AllocateAndInitializeSid( &sid_identifier_authority,
                                            1,
                                            SECURITY_LOCAL_SYSTEM_RID,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            0,
                                            &token_user.User.Sid ) )
    {
        PrintWin32Error( "AllocateAndInitializeSid() failed", GetLastError() );
        goto CreateSystemToken_exit;
    }
    token_owner.Owner = token_user.User.Sid;
    AllocateLocallyUniqueId( &token_source.SourceIdentifier );
    if ( NULL == ( token_statistics = ( PTOKEN_STATISTICS )GetFromToken( CurrentProcessToken, TokenStatistics ) ) )
    {
        goto CreateSystemToken_exit;
    }
    token_privileges = GetFromToken( CurrentProcessToken, TokenPrivileges );
    for ( PrivilegeCount = 0; PrivilegeCount < token_privileges->PrivilegeCount; PrivilegeCount++ )
    {
        /*
         * BOOL LookupPrivilegeName
         * (
         *     LPCTSTR lpSystemName,
         *     PLUID   lpLuid,
         *     LPTSTR  lpName,
         *     LPDWORD cbName
         * );
         *
         * BOOL LookupPrivilegeDisplayName
         * (
         *     LPCTSTR lpSystemName,
         *     LPCTSTR lpName,
         *     LPTSTR  lpDisplayName,
         *     LPDWORD cbDisplayName,
         *     LPDWORD lpLanguageId
         * );
         */
        NameSize = sizeof( PrivilegeName );
        if ( FALSE == LookupPrivilegeName( NULL, &token_privileges->Privileges[PrivilegeCount].Luid,
                                           PrivilegeName, &NameSize ) )
        {
            PrintWin32Error( "LookupPrivilegeName() failed", GetLastError() );
            goto CreateSystemToken_exit;
        }
        NameSize = sizeof( PrivilegeDisplayName );
        if ( FALSE == LookupPrivilegeDisplayName( NULL, PrivilegeName, PrivilegeDisplayName,
                                                  &NameSize, &LanguageId ) )
        {
            PrintWin32Error( "LookupPrivilegeDisplayName() failed", GetLastError() );
            goto CreateSystemToken_exit;
        }
        printf( "%40s (%s)\n", PrivilegeDisplayName, PrivilegeName );
    }  /* end of for */
    status = ZwCreateToken
             (
                 &SystemToken,
                 TOKEN_ALL_ACCESS,
                 &object_attributes,
                 TokenPrimary,
                 // &token_statistics->AuthenticationId,
                 &AuthenticationId,
                 &token_statistics->ExpirationTime,
                 &token_user,
                 GetFromToken( CurrentProcessToken, TokenGroups ),
                 //GetFromToken( CurrentProcessToken, TokenPrivileges ),
                 token_privileges,
                 //
                 // GetFromToken( CurrentProcessToken, TokenOwner ),
                 //
                 // XP中如果像上面这样编码,就会导致如下错误信息,
                 // ZwCreateToken() failed: 这个安全 ID 不能指派为此对象的所有者。
                 // 2K中可以像上面这样编码。
                 //
                 &token_owner,
                 GetFromToken( CurrentProcessToken, TokenPrimaryGroup ),
                 GetFromToken( CurrentProcessToken, TokenDefaultDacl ),
                 &token_source
             );
    if ( !NT_SUCCESS( status ) )
    {
        PrintZwError( "ZwCreateToken() failed", status );
        goto CreateSystemToken_exit;
    }

CreateSystemToken_exit:

    if ( token_user.User.Sid != NULL )
    {
        FreeSid( token_user.User.Sid );
        token_user.User.Sid = NULL;
    }
    if ( CurrentProcessToken != NULL )
    {
        CloseHandle( CurrentProcessToken );
        CurrentProcessToken = NULL;
    }
    return( SystemToken );
}  /* end of CreateSystemToken */

static BOOL DisableCurrentProcessSomePrivilege ( void )
{
    SetCurrentProcessPrivilege( SE_CREATE_TOKEN_NAME, FALSE );
    SetCurrentProcessPrivilege( SE_ASSIGNPRIMARYTOKEN_NAME, FALSE );
    RemoveCurrentProcessPrivilege( L"SeCreateTokenPrivilege" );
    RemoveCurrentProcessPrivilege( L"SeAssignPrimaryTokenPrivilege" );
    return( TRUE );
}  /* end of DisableCurrentProcessSomePrivilege */

static BOOL EnableCurrentProcessSomePrivilege ( void )
{
    /*
     * 对于2K、XP来说,这两种特权缺省并未赋予Administrator用户。下面这两行
     * 代码也就是"本地安全策略/用户权利指派"所做的事,因此同样需要注销重登
     * 录后才能生效,实际上失去了利用价值。
     */
    AddCurrentProcessPrivilege( L"SeCreateTokenPrivilege" );
    AddCurrentProcessPrivilege( L"SeAssignPrimaryTokenPrivilege" );
    SetCurrentProcessPrivilege( SE_CREATE_TOKEN_NAME, TRUE );
    SetCurrentProcessPrivilege( SE_ASSIGNPRIMARYTOKEN_NAME, TRUE );
    return( TRUE );
}  /* end of EnableCurrentProcessSomePrivilege */

static PVOID GetFromToken ( HANDLE TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass )
{
    DWORD needed  = 0;
    PVOID buf     = NULL;
    DWORD error;
    BOOL  errflag = FALSE;

    /*
     * BOOL GetTokenInformation
     * (
     *     HANDLE                  TokenHandle,
     *     TOKEN_INFORMATION_CLASS TokenInformationClass,
     *     LPVOID                  TokenInformation,
     *     DWORD                   TokenInformationLength,
     *     PDWORD                  ReturnLength
     * );
     */
    if ( FALSE == GetTokenInformation( TokenHandle, TokenInformationClass, NULL, 0, &needed ) )
    {
        error = GetLastError();
        if ( error != ERROR_INSUFFICIENT_BUFFER )
        {
            PrintWin32Error( "GetTokenInformation() failed", error );
            errflag = TRUE;
            goto GetFromToken_exit;
        }
    }
    if ( NULL == ( buf = calloc( needed, 1 ) ) )
    {
        fprintf( stderr, "calloc( %u, 1 ) failed\n", needed );
        goto GetFromToken_exit;
    }
    if ( FALSE == GetTokenInformation( TokenHandle, TokenInformationClass, buf, needed, &needed ) )
    {
        PrintWin32Error( "GetTokenInformation() failed", GetLastError() );
        errflag = TRUE;
        goto GetFromToken_exit;
    }

GetFromToken_exit:

    if ( errflag == TRUE )
    {
        if ( buf != NULL )
        {
            free( buf );
            buf = NULL;
        }
    }
    return( buf );
}  /* end of GetFromToken */

/*
* ntdll.dll输出了所有的Native API
*/
static BOOL LocateNtdllEntry ( void )
{
    BOOLEAN bool_ret    = FALSE;
    char    NTDLL_DLL[] = "ntdll.dll";
    HMODULE ntdll_dll   = NULL;

    /*
     * returns a handle to a mapped module without incrementing its
     * reference count
     */
    if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
    {
        PrintWin32Error( "GetModuleHandle() failed", GetLastError() );
        return( FALSE );
    }
    if ( !( RtlNtStatusToDosError = ( RTLNTSTATUSTODOSERROR )GetProcAddress( ntdll_dll, "RtlNtStatusToDosError" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    if ( !( ZwCreateToken = ( ZWCREATETOKEN )GetProcAddress( ntdll_dll, "ZwCreateToken" ) ) )
    {
        goto LocateNtdllEntry_return;
    }
    bool_ret = TRUE;

LocateNtdllEntry_return:

    if ( FALSE == bool_ret )
    {
        PrintWin32Error( "GetProcAddress() failed", GetLastError() );
    }
    ntdll_dll = NULL;
    return( bool_ret );
}  /* end of LocateNtdllEntry */

static void PrintWin32Error ( char *message, DWORD dwMessageId )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   dwMessageId,
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32Error */

static void PrintZwError ( char *message, NTSTATUS status )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   RtlNtStatusToDosError( status ),
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintZwError */

static BOOL RemoveCurrentProcessPrivilege ( LPWSTR PrivilegeName )
{
    NTSTATUS              status;
    BOOL                  ret                 = FALSE;
    LSA_HANDLE            PolicyHandle        = NULL;
    LSA_OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE                CurrentProcessToken = NULL;
    PTOKEN_USER           token_user          = NULL;

    ZeroMemory( &ObjectAttributes, sizeof( ObjectAttributes ) );
    /*
     * NTSTATUS LsaOpenPolicy
     * (
     *     PLSA_UNICODE_STRING    SystemName,
     *     PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
     *     ACCESS_MASK            DesiredAccess,
     *     PLSA_HANDLE            PolicyHandle
     * );
     */
    status = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &PolicyHandle );
    if ( status != STATUS_SUCCESS )
    {
        PrintWin32Error( "LsaOpenPolicy() failed", LsaNtStatusToWinError( status ) );
        goto RemoveCurrentProcessPrivilege_exit;
    }
    if ( FALSE == OpenProcessToken( GetCurrentProcess(),
                                    TOKEN_QUERY,
                                    &CurrentProcessToken ) )
    {
        PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
        goto RemoveCurrentProcessPrivilege_exit;
    }
    if ( NULL == ( token_user = ( PTOKEN_USER )GetFromToken( CurrentProcessToken, TokenUser ) ) )
    {
        goto RemoveCurrentProcessPrivilege_exit;
    }
    if ( FALSE == RemovePrivilege( PolicyHandle,
                                   token_user->User.Sid,
                                   PrivilegeName ) )
    {
        goto RemoveCurrentProcessPrivilege_exit;
    }
    ret    = TRUE;

RemoveCurrentProcessPrivilege_exit:

    if ( NULL != token_user )
    {
        free( token_user );
        token_user = NULL;
    }
    if ( NULL != CurrentProcessToken )
    {
        CloseHandle( CurrentProcessToken );
        CurrentProcessToken = NULL;
    }
    if ( NULL != PolicyHandle )
    {
        LsaClose( PolicyHandle );
        PolicyHandle = NULL;
    }
    return( ret );
}  /* end of RemoveCurrentProcessPrivilege */

static BOOL RemovePrivilege ( LSA_HANDLE PolicyHandle, PSID AccountSid, LPWSTR PrivilegeName )
{
    BOOL               ret = FALSE;
    LSA_UNICODE_STRING UserRights;
    USHORT             StringLength;
    NTSTATUS           status;

    if ( PrivilegeName == NULL )
    {
        goto RemovePrivilege_exit;
    }
    StringLength             = wcslen( PrivilegeName );
    UserRights.Buffer        = PrivilegeName;
    UserRights.Length        = StringLength * sizeof( WCHAR );
    UserRights.MaximumLength = ( StringLength + 1 ) * sizeof( WCHAR );
    /*
     * Header : Declared in Ntsecapi.h.
     * Library: Use Advapi32.lib.
     *
     * NTSTATUS LsaRemoveAccountRights
     * (
     *     LSA_HANDLE          PolicyHandle,
     *     PSID                AccountSid,
     *     BOOLEAN             AllRights,
     *     PLSA_UNICODE_STRING UserRights,
     *     ULONG               CountOfRights
     * );
     */
    status                   = LsaRemoveAccountRights( PolicyHandle, AccountSid, FALSE, &UserRights, 1 );
    if ( status != STATUS_SUCCESS )
    {
        PrintWin32Error( "LsaRemoveAccountRights() failed", LsaNtStatusToWinError( status ) );
        goto RemovePrivilege_exit;
    }
    ret                      = TRUE;

RemovePrivilege_exit:

    return( ret );
}  /* end of RemovePrivilege */

static BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag )
{
    HANDLE TokenHandle = ( HANDLE )-1;
    BOOL   ret         = TRUE;

    /*
     * Header : Declared in Winbase.h; include Windows.h.
     * Library: Use Advapi32.lib.
     *
     * BOOL OpenProcessToken
     * (
     *     HANDLE  ProcessHandle,
     *     DWORD   DesiredAccess,
     *     PHANDLE TokenHandle
     * );
     */
    if ( FALSE == OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle ) )
    {
        PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
        ret = FALSE;
        goto SetCurrentProcessPrivilege_exit;
    }
    ret = SetPrivilege( TokenHandle, PrivilegeName, EnableFlag );

SetCurrentProcessPrivilege_exit:

    if ( TokenHandle != ( HANDLE )-1 )
    {
        CloseHandle( TokenHandle );
        TokenHandle = ( HANDLE )-1;
    }
    return( ret );
}  /* end of SetCurrentProcessPrivilege */

static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag )
{
    DWORD            error;
    BOOL             ret = TRUE;
    /*
     *
     * typedef struct _TOKEN_PRIVILEGES
     * {
     *     DWORD               PrivilegeCount;
     *     LUID_AND_ATTRIBUTES Privileges[];
     * } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
     *
     * typedef struct _LUID_AND_ATTRIBUTES
     * {
     *     LUID  Luid;
     *     DWORD Attributes;
     * } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
     */
    TOKEN_PRIVILEGES tp  =
    {
        1,
        {
            { { 0, 0 }, 0 }
        }
    };

    if ( EnableFlag == TRUE )
    {
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    }
    /*
     * BOOL LookupPrivilegeValue
     * (
     *     LPCTSTR lpSystemName,
     *     LPCTSTR lpName,
     *     PLUID   lpLuid
     * );
     *
     * 第二形参的可取值在winnt.h中有定义,"NT Defined Privileges"
     */
    if ( FALSE == LookupPrivilegeValue( NULL, PrivilegeName, &tp.Privileges[0].Luid ) )
    {
        PrintWin32Error( "LookupPrivilegeValue() failed", GetLastError() );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
    /*
     * BOOL AdjustTokenPrivileges
     * (
     *     HANDLE            TokenHandle,
     *     BOOL              DisableAllPrivileges,
     *     PTOKEN_PRIVILEGES NewState,
     *     DWORD             BufferLength,
     *     PTOKEN_PRIVILEGES PreviousState,
     *     PDWORD            ReturnLength
     * );
     *
     * The AdjustTokenPrivileges function cannot add new privileges to the
     * access token. It can only enable or disable the token's existing
     * privileges. To determine the token's privileges, call the
     * GetTokenInformation function.
     */
    if ( FALSE == AdjustTokenPrivileges( TokenHandle, FALSE, &tp, sizeof( tp ), NULL, NULL ) )
    {
        PrintWin32Error( "AdjustTokenPrivileges() failed", GetLastError() );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
    else
    {
        error = GetLastError();
        /*
         * 这种情况带来的误判很隐蔽,务必留心。
         *
         * ERROR_NOT_ALL_ASSIGNED
         */
        if ( ERROR_SUCCESS != error )
        {
            PrintWin32Error( "AdjustTokenPrivileges() failed", error );
            ret = FALSE;
            goto SetPrivilege_exit;
        }
    }

SetPrivilege_exit:

    return( ret );
}  /* end of SetPrivilege */

int main ( int argc, char * argv[] )
{
    STARTUPINFO         si = { sizeof( si ) };
    PROCESS_INFORMATION pi;

    if ( 2 != argc )
    {
        fprintf( stderr, "Usage: %s <command line>\n", argv[0] );
        return( EXIT_FAILURE );
    }
    if ( FALSE == LocateNtdllEntry() )
    {
        fprintf( stderr, "LocateNtdllEntry() failed\n" );
        return( EXIT_FAILURE );
    }
    EnableCurrentProcessSomePrivilege();
    /*
     * Header : Declared in Winbase.h; include Windows.h.
     * Library: Use Advapi32.lib.
     *
     * BOOL CreateProcessAsUser
     * (
     *     HANDLE                hToken,               // handle to user token
     *     LPCTSTR               lpApplicationName,    // name of executable module
     *     LPTSTR                lpCommandLine,        // command-line string
     *     LPSECURITY_ATTRIBUTES lpProcessAttributes,  // SD
     *     LPSECURITY_ATTRIBUTES lpThreadAttributes,   // SD
     *     BOOL                  bInheritHandles,      // inheritance option
     *     DWORD                 dwCreationFlags,      // creation flags
     *     LPVOID                lpEnvironment,        // new environment block
     *     LPCTSTR               lpCurrentDirectory,   // current directory name
     *     LPSTARTUPINFO         lpStartupInfo,        // startup information
     *     LPPROCESS_INFORMATION lpProcessInformation  // process information
     * );
     *
     * Typically, the process that calls the CreateProcessAsUser function
     * must have the SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME
     * privileges.
     */
    if ( FALSE == CreateProcessAsUser
                  (
                      CreateSystemToken(),
                      NULL,
                      argv[1],
                      NULL,
                      NULL,
                      FALSE,
                      CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
                      NULL,
                      NULL,
                      &si,
                      &pi
                  ) )
    {
        PrintWin32Error( "CreateProcessAsUser() failed", GetLastError() );
        return( EXIT_FAILURE );
    }
    /*
     * CreateProcessAsUser()无论是否成功,都返回。
     */
    DisableCurrentProcessSomePrivilege();
    /*
     * 主调进程马上要退出了,懒得显式关闭PROCESS_INFORMATION中的句柄,由操
     * 作系统隐式关闭去吧。事实上CreateSystemToken()中调用GetFromToken(),
     * 后者在堆区分配过内存,不过这次也不需要显式释放了。
     */
    return( EXIT_SUCCESS );
}  /* end of main */

/************************************************************************/

--------------------------------------------------------------------------

如果第一次执行失败,注销/重登录再次执行即可成功。如果第一次执行成功,注销/
重登录再次执行将会失败。这只是演示如何编程实现"本地安全策略/用户权利指派"。

执行regedit,如果能看到下列内容,表示已经获取SYSTEM权限:

HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Builtin

缺省情况下,管理员帐号只能看到:

HKEY_LOCAL_MACHINE\SAM\SAM

个人准备将sysproc.c扔进垃圾堆,离我所需相去甚远。

☆ sysproc_now.c

麻雀兄解释了Ashot Oganesyan K的中心思想,Win32 API/CreateProcess()最终会调
用Native API/ZwCreateProcess(),后者的第四形参是InheritFromProcessHandle,
子进程的主令牌(Primary Token)继承自这个进程句柄,一般情况下也就是父进程句
柄。将ZwCreateProcess()拦截(Hook),修改第四形参,使之对应一个拥有SYSTEM权
限的进程句柄,于是所创建的子进程将拥有SYSTEM权限。

我主要做了两处改进。一是利用ZwQuerySystemInformation()自己找出winlogon.exe
的PID,并获取进程句柄,从而不必在命令行上指定拥有SYSTEM权限的PID。二是增加
对XP、2003 Server的支持。

一开始我拦截ZwCreateProcess(),发现对于XP无效,怀疑CreateProcess()已经不调
用ZwCreateProcess()。增加了一点调试代码,确认流程并未经过ZwCreateProcess()。
然后我就到处乱找能跟踪系统调用的工具,最后用了BindView的strace工具([1])。
0.3版的strace为了在XP上使用,需要在注册表中做如下设置,重启后生效:

--------------------------------------------------------------------------
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management]
"EnforceWriteProtection"=dword:00000000
--------------------------------------------------------------------------

跟踪结果中只有NtCreateProcessEx(),而没有NtCreateProcess()。于是我换成拦截
ZwCreateProcessEx(),搞定。

不清楚ZwCreateProcessEx()的函数原型是什么,形参为九个,比ZwCreateProcess()
多一个。按照MS的一贯作风,应该不会动前八个形参,仅仅是扩展出第九个形参,因
此第四形参仍为InheritFromProcessHandle。ZwCreateProcess()的函数原型可参看
[5]。顺便说一句,编写、侦测rootkit时要留心XP/2003上的这种变化。

为了区分2000/XP/2003,用GetVersionEx()做精确的OS版本判断。用IDA Pro 4.3.0
反汇编ntdll.dll即可找出下列信息:

--------------------------------------------------------------------------
XP

    ZwCreateProcess   2Fh
    ZwCreateProcessEx 30h

2003 Server

    ZwCreateProcess   31h
    ZwCreateProcessEx 32h
--------------------------------------------------------------------------

可能你看到的不是熟悉的int 2Eh,而类似如下代码:

--------------------------------------------------------------------------
ZwCreateProcessEx proc near
    mov     eax, 32h
    mov     edx, 7FFE0300h
    call    edx
    retn    24h
ZwCreateProcessEx endp
--------------------------------------------------------------------------

0x7FFE0300处是什么东西,用kd查看一下:

--------------------------------------------------------------------------
lkd> u 0x7ffe0300
SharedUserData!SystemCallStub:
7ffe0300 8bd4             mov     edx,esp
7ffe0302 0f34             sysenter
7ffe0304 c3               ret
--------------------------------------------------------------------------

这就是所谓"快速系统调用接口"。参看Intel卷II中对sysenter指令的解释。VC 7不
支持sysenter,我用_emit直接嵌入机器码。从Windows 2000/PII开始引入这个与传
统int 2Eh并存的快速系统调用接口,但你硬是要用int 2Eh仍然可以成功。ntoskrnl
中与这两种系统调用接口相对应的是KiFastCallEntry()、KiSystemService()。

--------------------------------------------------------------------------
lkd> u nt!KiFastCallEntry
nt!KiFastCallEntry:
8052d480 368b0d40f0dfff   mov     ecx,ss:[ffdff040]
8052d487 368b6104         mov     esp,ss:[ecx+0x4]
8052d48b b90403fe7f       mov     ecx,0x7ffe0304
8052d490 3b2504f0dfff     cmp     esp,[ffdff004]
8052d496 0f84ce030000     je      nt!KiServiceExit2+0x143 (8052d86a)
8052d49c 6a23             push    0x23
8052d49e 52               push    edx
8052d49f 83c208           add     edx,0x8
lkd> u nt!KiSystemService
nt!KiSystemService:
8052d4ad 6a00             push    0x0
8052d4af 55               push    ebp
8052d4b0 53               push    ebx
8052d4b1 56               push    esi
8052d4b2 57               push    edi
8052d4b3 0fa0             push    fs
8052d4b5 bb30000000       mov     ebx,0x30
8052d4ba 668ee3           mov     fs,bx
lkd>
--------------------------------------------------------------------------

要想拦截KiFastCallEntry(),可比拦截KiSystemService()复杂一些,关于这个就不
多解释了,反正这次的主要目的也不是拦截内核空间的什么东东。不过务必要记住这
件事,只拦截KiSystemService()很可能会得出错误结论。

[6]是一份不错的关于此方面内容的文档。

sysproc_now.c中演示了这两种系统调用接口,但是用条件编译注释掉了其中一种,
你可以根据自己的爱好选用任一种,均可成功达到目的。

--------------------------------------------------------------------------
/*
* For x86/EWindows XP SP1 & VC 7
* cl sysproc_now.c /Os /G6 /W3
*
* Support for Windows NT3.51 NT4.0 2000
* (c)1999 Ashot Oganesyan K, SmartLine, Inc
* Ashot Oganesyan K <ashot@aha.ru>
*
* Add support for Windows XP/2003 by scz <scz@nsfocus.com>
* 2003-06-21 21:22
*/

/************************************************************************
*                                                                      *
*                               Head File                              *
*                                                                      *
************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

/************************************************************************
*                                                                      *
*                               Macro                                  *
*                                                                      *
************************************************************************/

#pragma comment( linker, "/subsystem:console" )
#pragma comment( lib,    "kernel32.lib"       )
#pragma comment( lib,    "advapi32.lib"       )

typedef LONG NTSTATUS;

/*
* you'll find a list of NTSTATUS status codes in the DDK header
* ntstatus.h (\WINDDK\2600.1106\inc\ddk\wxp\)
*/
#define NT_SUCCESS(status)          ((NTSTATUS)(status)>=0)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)

/*
*************************************************************************
* ntddk.h
*/

typedef LONG KPRIORITY;

/*
* ntddk.h
*************************************************************************
*/

/*
*************************************************************************
* ntdef.h
*/

typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

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

/*
* ntdef.h
*************************************************************************
*/

/*
*************************************************************************
* <<Windows NT/2000 Native API Reference>> - Gary Nebbett
*/

typedef enum _SYSTEM_INFORMATION_CLASS
{
    SystemProcessesAndThreadsInformation = 5
} SYSTEM_INFORMATION_CLASS;

/*
* Information Class 5
*
* 为了便于编译,我将InheritedFromProcessId之后的成员扔掉了
*/
typedef struct _SYSTEM_PROCESSES
{
    ULONG          NextEntryDelta;
    ULONG          ThreadCount;
    ULONG          Reserved1[6];
    LARGE_INTEGER  CreateTime;
    LARGE_INTEGER  UserTime;
    LARGE_INTEGER  KernelTime;
    UNICODE_STRING ProcessName;
    KPRIORITY      BasePriority;
    ULONG          ProcessId;
    ULONG          InheritedFromProcessId;
} SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;

/*
* <<Windows NT/2000 Native API Reference>> - Gary Nebbett
*************************************************************************
*/

/*
* 参看DDK文档以及<<Windows NT/2000 Native API Reference>> - Gary Nebbett
* 这些Native API由ntdll.dll输出
*/
typedef ULONG    ( __stdcall *RTLNTSTATUSTODOSERROR    ) ( IN NTSTATUS Status );
typedef NTSTATUS ( __stdcall *ZWCREATEPROCESS          ) ( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE InheritFromProcessHandle, IN BOOLEAN InheritHandles, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL );
typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );

#pragma pack(push, 1)

typedef struct
{
    unsigned char       mov_eax;
    void               *address;
    unsigned short int  jmp_eax;
} ASMJUMP, *PASMJUMP;

#pragma pack(pop)

/************************************************************************
*                                                                      *
*                            Function Prototype                        *
*                                                                      *
************************************************************************/

static BOOL  DisableCurrentProcessDebugPrivilege
                              ( void );
static BOOL  EnableCurrentProcessDebugPrivilege
                              ( void );
static DWORD GetPidFromProcessName
                              ( wchar_t *ProcessName );
static BOOL  LocateNtdllEntry ( void );
static void  PrintWin32Error  ( char *message, DWORD dwMessageId );
static void  PrintZwError     ( char *message, NTSTATUS status );
static BOOL  SetCurrentProcessPrivilege
                              ( LPCTSTR PrivilegeName, BOOL EnableFlag );
static BOOL  SetPrivilege     ( HANDLE TokenHandle, LPCTSTR PrivilegeName,
                                BOOL EnableFlag );

/************************************************************************
*                                                                      *
*                            Static Global Var                         *
*                                                                      *
************************************************************************/

/*
* 由ntdll.dll输出的Native API函数指针
*/
static RTLNTSTATUSTODOSERROR    RtlNtStatusToDosError    = NULL;
static ZWCREATEPROCESS          ZwCreateProcess          = NULL;
static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;
/*
* 这是XP/2003新增的Native API,总共九个形参,比ZwCreateProcess()多一个
*/
static PVOID                    ZwCreateProcessEx        = NULL;

/*
* winlogon.exe的进程句柄
*/
static HANDLE                   winlogon                 = NULL;
static DWORD                    ZwCreateProcessNum       = 0;
static DWORD                    ZwCreateProcessExNum     = 0;

/************************************************************************/

static BOOL DisableCurrentProcessDebugPrivilege ( void )
{
    return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, FALSE ) );
}  /* end of DisableCurrentProcessDebugPrivilege */

static BOOL EnableCurrentProcessDebugPrivilege ( void )
{
    return( SetCurrentProcessPrivilege( SE_DEBUG_NAME, TRUE ) );
}  /* end of EnableCurrentProcessDebugPrivilege */

static DWORD GetPidFromProcessName ( wchar_t *ProcessName )
{
    NTSTATUS          status;
    PVOID             buf   = NULL;
    ULONG             size  = 1;
    PSYSTEM_PROCESSES proc  = NULL;
    ULONG             delta = 0;
    DWORD             pid   = 0;

    for ( size = 1; ; size *= 2 )
    {
        if ( NULL == ( buf = calloc( size, 1 ) ) )
        {
            fprintf( stderr, "calloc( %u, 1 ) failed\n", size );
            goto GetPidFromProcessName_exit;
        }
        status = ZwQuerySystemInformation( SystemProcessesAndThreadsInformation, buf, size, NULL );
        if ( !NT_SUCCESS( status ) )
        {
            if ( STATUS_INFO_LENGTH_MISMATCH == status )
            {
                free( buf );
                buf = NULL;
            }
            else
            {
                PrintZwError( "ZwQuerySystemInformation() failed", status );
                goto GetPidFromProcessName_exit;
            }
        }
        else
        {
            /*
             * printf( "size = %u\n", size );
             */
            break;
        }
    }  /* end of for */
    proc = ( PSYSTEM_PROCESSES )buf;
    do
    {
        if ( NULL != proc->ProcessName.Buffer )
        {
            if ( 0 == _wcsicmp( ProcessName, proc->ProcessName.Buffer ) )
            {
                pid = proc->ProcessId;
                break;
            }
        }
        delta = proc->NextEntryDelta;
        proc  = ( PSYSTEM_PROCESSES )( ( char * )proc + delta );
    }
    while ( 0 != delta );

GetPidFromProcessName_exit:

    if ( buf != NULL )
    {
        free( buf );
        buf = NULL;
    }
    return( pid );
}  /* end of GetPidFromProcessName */

static void __declspec(naked) HackedZwCreateProcess ( void )
{
    /*
     *********************************************************************
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     */
    __asm
    {
        pushad
        pushfd
    }

    printf( "HackedZwCreateProcess()\n" );

    __asm
    {
        popfd
        popad
    }
    /*
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     *********************************************************************
     */

    /*
     * 第四形参是InheritFromProcessHandle
     * [esp]是返回地址
     */
    __asm
    {
        mov     eax,winlogon
        mov     dword ptr [esp+16],eax
        /*
         * EAX是调用号
         */
        mov     eax,ZwCreateProcessNum
        /*
         * EDX指向形参区域
         */
        lea     edx,dword ptr [esp+4]
        int     2Eh
        /*
         * __stdcall调用风格,由被调函数自己负责堆栈平衡。
         */
        retn    20h
    }
}  /* end of HackedZwCreateProcess */

#if 0
static void __declspec(naked) HackedZwCreateProcessEx ( void )
{
    /*
     *********************************************************************
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     */
    __asm
    {
        pushad
        pushfd
    }

    printf( "HackedZwCreateProcessEx()\n" );

    __asm
    {
        popfd
        popad
    }
    /*
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     *********************************************************************
     */

    /*
     * 第四形参是InheritFromProcessHandle
     * [esp]是返回地址
     */
    __asm
    {
        mov     eax,winlogon
        mov     dword ptr [esp+16],eax
        /*
         * EAX是调用号
         */
        mov     eax,ZwCreateProcessExNum
        /*
         * EDX指向形参区域
         */
        lea     edx,dword ptr [esp+4]
        int     2Eh
        /*
         * __stdcall调用风格,由被调函数自己负责堆栈平衡。注意这次有九个形
         * 参,平衡堆栈时要比前面多弹一个DWORD。
         */
        retn    24h
    }
}  /* end of HackedZwCreateProcessEx */
#endif

#if 0
static void __declspec(naked) HackedZwCreateProcessEx ( void )
{
    /*
     *********************************************************************
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     */
    __asm
    {
        pushad
        pushfd
    }

    printf( "HackedZwCreateProcessEx()\n" );

    __asm
    {
        popfd
        popad
    }
    /*
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     *********************************************************************
     */

    /*
     * 第四形参是InheritFromProcessHandle
     * [esp]是返回地址
     */
    __asm
    {
        mov     eax,winlogon
        mov     dword ptr [esp+16],eax
        /*
         * EAX是调用号
         */
        mov     eax,ZwCreateProcessExNum
        /*
         * lkd> u 0x7ffe0300
         * SharedUserData!SystemCallStub:
         * 7ffe0300 8bd4             mov     edx,esp
         * 7ffe0302 0f34             sysenter
         * 7ffe0304 c3               ret
         */
        mov     edx,7FFE0300h
        call    edx
        /*
         * __stdcall调用风格,由被调函数自己负责堆栈平衡。注意这次有九个形
         * 参,平衡堆栈时要比前面多弹一个DWORD。
         */
        retn    24h
    }
}  /* end of HackedZwCreateProcessEx */
#endif

/*
* 上面被条件编译注释掉的两段代码均可工作于XP/2003上。int 2Eh是传统系统调
* 用接口,sysenter是所谓"快速系统调用接口",参看Intel卷II中对该指令的解释。
* VC 7不支持sysenter,我用_emit直接嵌入机器码。
*/
static void __declspec(naked) HackedZwCreateProcessEx ( void )
{
    /*
     *********************************************************************
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     */
    __asm
    {
        pushad
        pushfd
    }

    printf( "HackedZwCreateProcessEx()\n" );

    __asm
    {
        popfd
        popad
    }
    /*
     * 这两个__asm{}之间的代码只是调试代码,没有实际意义。
     *********************************************************************
     */

    /*
     * 第四形参是InheritFromProcessHandle
     * [esp]是返回地址
     */
    __asm
    {
        mov     eax,winlogon
        mov     dword ptr [esp+16],eax
        /*
         * EAX是调用号
         */
        mov     eax,ZwCreateProcessExNum
        call    SystemCallStub
        retn    24h

SystemCallStub:

        mov     edx,esp
        /*
         * 0f34 sysenter
         */
        _emit   0x0f
        _emit   0x34
        ret
    }
}  /* end of HackedZwCreateProcessEx */

/*
* ntdll.dll输出了所有的Native API
*/
static BOOL LocateNtdllEntry ( void )
{
    BOOL    ret         = FALSE;
    char    NTDLL_DLL[] = "ntdll.dll";
    HMODULE ntdll_dll   = NULL;

    /*
     * returns a handle to a mapped module without incrementing its
     * reference count
     */
    if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
    {
        PrintWin32Error( "GetModuleHandle() failed", GetLastError() );
        return( FALSE );
    }
    if ( !( RtlNtStatusToDosError = ( RTLNTSTATUSTODOSERROR )GetProcAddress( ntdll_dll, "RtlNtStatusToDosError" ) ) )
    {
        goto LocateNtdllEntry_exit;
    }
    if ( !( ZwCreateProcess = ( ZWCREATEPROCESS )GetProcAddress( ntdll_dll, "ZwCreateProcess" ) ) )
    {
        goto LocateNtdllEntry_exit;
    }
    if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, "ZwQuerySystemInformation" ) ) )
    {
        goto LocateNtdllEntry_exit;
    }
    if ( ZwCreateProcessExNum != 0 )
    {
        if ( !( ZwCreateProcessEx = ( PVOID )GetProcAddress( ntdll_dll, "ZwCreateProcessEx" ) ) )
        {
            goto LocateNtdllEntry_exit;
        }
    }
    ret = TRUE;

LocateNtdllEntry_exit:

    if ( FALSE == ret )
    {
        PrintWin32Error( "GetProcAddress() failed", GetLastError() );
    }
    ntdll_dll = NULL;
    return( ret );
}  /* end of LocateNtdllEntry */

static void PrintWin32Error ( char *message, DWORD dwMessageId )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   dwMessageId,
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintWin32Error */

static void PrintZwError ( char *message, NTSTATUS status )
{
    char *errMsg;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
                   RtlNtStatusToDosError( status ),
                   MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
                   ( LPTSTR )&errMsg, 0, NULL );
    fprintf( stderr, "%s: %s", message, errMsg );
    LocalFree( errMsg );
    return;
}  /* end of PrintZwError */

static BOOL SetCurrentProcessPrivilege ( LPCTSTR PrivilegeName, BOOL EnableFlag )
{
    HANDLE TokenHandle = ( HANDLE )-1;
    BOOL   ret         = TRUE;

    /*
     * Header : Declared in Winbase.h; include Windows.h.
     * Library: Use Advapi32.lib.
     *
     * BOOL OpenProcessToken
     * (
     *     HANDLE  ProcessHandle,
     *     DWORD   DesiredAccess,
     *     PHANDLE TokenHandle
     * );
     */
    if ( FALSE == OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &TokenHandle ) )
    {
        PrintWin32Error( "OpenProcessToken() failed", GetLastError() );
        ret = FALSE;
        goto SetCurrentProcessPrivilege_exit;
    }
    ret = SetPrivilege( TokenHandle, PrivilegeName, EnableFlag );

SetCurrentProcessPrivilege_exit:

    if ( TokenHandle != ( HANDLE )-1 )
    {
        CloseHandle( TokenHandle );
        TokenHandle = ( HANDLE )-1;
    }
    return( ret );
}  /* end of SetCurrentProcessPrivilege */

static BOOL SetPrivilege ( HANDLE TokenHandle, LPCTSTR PrivilegeName, BOOL EnableFlag )
{
    DWORD            error;
    BOOL             ret = TRUE;
    /*
     *
     * typedef struct _TOKEN_PRIVILEGES
     * {
     *     DWORD               PrivilegeCount;
     *     LUID_AND_ATTRIBUTES Privileges[];
     * } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
     *
     * typedef struct _LUID_AND_ATTRIBUTES
     * {
     *     LUID  Luid;
     *     DWORD Attributes;
     * } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
     */
    TOKEN_PRIVILEGES tp  =
    {
        1,
        {
            { { 0, 0 }, 0 }
        }
    };

    if ( EnableFlag == TRUE )
    {
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    }
    /*
     * BOOL LookupPrivilegeValue
     * (
     *     LPCTSTR lpSystemName,
     *     LPCTSTR lpName,
     *     PLUID   lpLuid
     * );
     *
     * 第二形参的可取值在winnt.h中有定义,"NT Defined Privileges"
     */
    if ( FALSE == LookupPrivilegeValue( NULL, PrivilegeName, &tp.Privileges[0].Luid ) )
    {
        PrintWin32Error( "LookupPrivilegeValue() failed", GetLastError() );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
    /*
     * BOOL AdjustTokenPrivileges
     * (
     *     HANDLE            TokenHandle,
     *     BOOL              DisableAllPrivileges,
     *     PTOKEN_PRIVILEGES NewState,
     *     DWORD             BufferLength,
     *     PTOKEN_PRIVILEGES PreviousState,
     *     PDWORD            ReturnLength
     * );
     *
     * The AdjustTokenPrivileges function cannot add new privileges to the
     * access token. It can only enable or disable the token's existing
     * privileges. To determine the token's privileges, call the
     * GetTokenInformation function.
     */
    if ( FALSE == AdjustTokenPrivileges( TokenHandle, FALSE, &tp, sizeof( tp ), NULL, NULL ) )
    {
        PrintWin32Error( "AdjustTokenPrivileges() failed", GetLastError() );
        ret = FALSE;
        goto SetPrivilege_exit;
    }
    else
    {
        error = GetLastError();
        /*
         * 这种情况带来的误判很隐蔽,务必留心。
         *
         * ERROR_NOT_ALL_ASSIGNED
         */
        if ( ERROR_SUCCESS != error )
        {
            PrintWin32Error( "AdjustTokenPrivileges() failed", error );
            ret = FALSE;
            goto SetPrivilege_exit;
        }
    }

SetPrivilege_exit:

    return( ret );
}  /* end of SetPrivilege */

int main ( int argc, char * argv[] )
{
#if 0
    DisableCurrentProcessDebugPrivilege();
    return( EXIT_SUCCESS );
#endif

    OSVERSIONINFO            osversioninfo;
    DWORD                    winlogonPid = 0;
    STARTUPINFO              si          = { sizeof( si ) };
    PROCESS_INFORMATION      pi;
    /*
     * typedef struct _MEMORY_BASIC_INFORMATION
     * {
     *     PVOID  BaseAddress;
     *     PVOID  AllocationBase;
     *     DWORD  AllocationProtect;
     *     SIZE_T RegionSize;
     *     DWORD  State;
     *     DWORD  Protect;
     *     DWORD  Type;
     * } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
     */
    MEMORY_BASIC_INFORMATION mbi;
    DWORD                    OldProtect;

    ZeroMemory( &pi, sizeof( pi ) );
    if ( 2 != argc )
    {
        fprintf( stderr, "Usage: %s <command line>\n", argv[0] );
        goto main_exit;
    }
    /*
     * typedef struct _OSVERSIONINFO
     * {
     *     DWORD dwOSVersionInfoSize;
     *     DWORD dwMajorVersion;
     *     DWORD dwMinorVersion;
     *     DWORD dwBuildNumber;
     *     DWORD dwPlatformId;
     *     TCHAR szCSDVersion[128];
     * } OSVERSIONINFO;
     *
     * BOOL GetVersionEx ( LPOSVERSIONINFO lpVersionInfo );
     */
    ZeroMemory( &osversioninfo, sizeof( osversioninfo ) );
    osversioninfo.dwOSVersionInfoSize = sizeof( osversioninfo );
    if ( FALSE == GetVersionEx( &osversioninfo ) )
    {
        PrintWin32Error( "GetVersionEx() failed", GetLastError() );
        goto main_exit;
    }
    printf( "Version %u.%u\n", osversioninfo.dwMajorVersion, osversioninfo.dwMinorVersion );
    if ( osversioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
    {
        if ( osversioninfo.dwMajorVersion == 3 && osversioninfo.dwMinorVersion == 51 )
        {
            /*
             * NT 3.51
             */
            ZwCreateProcessNum = 0x1E;
        }
        else if ( osversioninfo.dwMajorVersion == 4 && osversioninfo.dwMinorVersion == 0 )
        {
            /*
             * NT 4.0
             */
            ZwCreateProcessNum = 0x1F;
        }
        else if ( osversioninfo.dwMajorVersion == 5 && osversioninfo.dwMinorVersion == 0 )
        {
            /*
             * 2000
             */
            ZwCreateProcessNum = 0x29;
        }
        else if ( osversioninfo.dwMajorVersion == 5 && osversioninfo.dwMinorVersion == 1 )
        {
            /*
             * XP
             *
             * 与以前所有版本不同的是,Win32 API/CreateProcess()最终调用了
             * Native API/ZwCreateProcessEx(),因此Hook 0x2F无用。
             */
            ZwCreateProcessExNum = 0x30;
        }
        else if ( osversioninfo.dwMajorVersion == 5 && osversioninfo.dwMinorVersion == 2 )
        {
            /*
             * 2003
             */
            ZwCreateProcessExNum = 0x32;
        }
    }
    if ( ZwCreateProcessNum == 0 && ZwCreateProcessExNum == 0 )
    {
        fprintf( stderr, "Checking your Platform\n" );
        goto main_exit;
    }
    if ( FALSE == LocateNtdllEntry() )
    {
        fprintf( stderr, "LocateNtdllEntry() failed\n" );
        goto main_exit;
    }
    /*
     * 为了方便使用者,我们固定获取winlogon.exe进程句柄。
     */
    if ( 0 == ( winlogonPid = GetPidFromProcessName( L"winlogon.exe" ) ) )
    {
        fprintf( stderr, "GetPidFromProcessName() failed\n" );
        goto main_exit;
    }
    EnableCurrentProcessDebugPrivilege();
    /*
     * Header : Declared in Winbase.h; include Windows.h.
     * Library: Use Kernel32.lib.
     *
     * HANDLE OpenProcess
     * (
     *     DWORD dwDesiredAccess,  // access flag
     *     BOOL  bInheritHandle,   // handle inheritance option
     *     DWORD dwProcessId       // process identifier
     * );
     */
    if ( NULL == ( winlogon = OpenProcess( PROCESS_CREATE_PROCESS, TRUE, winlogonPid ) ) )
    {
        PrintWin32Error( "OpenProcess() failed", GetLastError() );
        goto main_exit;
    }
    /*
     * DWORD VirtualQuery
     * (
     *     LPCVOID                   lpAddress,  // address of region
     *     PMEMORY_BASIC_INFORMATION lpBuffer,   // information buffer
     *     SIZE_T                    dwLength    // size of buffer
     * );
     */
    ZeroMemory( &mbi, sizeof( mbi ) );
    if ( 0 != ZwCreateProcessNum )
    {
        VirtualQuery( ZwCreateProcess, &mbi, sizeof( mbi ) );
    }
    else
    {
        VirtualQuery( ZwCreateProcessEx, &mbi, sizeof( mbi ) );
    }
    /*
     * BOOL VirtualProtect
     * (
     *     LPVOID lpAddress,      // region of committed pages
     *     SIZE_T dwSize,         // size of the region
     *     DWORD  flNewProtect,   // desired access protection
     *     PDWORD lpflOldProtect  // old protection
     * );
     */
    if ( FALSE == VirtualProtect( mbi.AllocationBase, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &OldProtect ) )
    {
        PrintWin32Error( "VirtualProtect() failed", GetLastError() );
        goto main_exit;
    }
    if ( 0 != ZwCreateProcessNum )
    {
        /*
         * mov eax, HackedZwCreateProcess
         * jmp eax
         */
        ( ( PASMJUMP )ZwCreateProcess )->mov_eax = 0xB8;
        ( ( PASMJUMP )ZwCreateProcess )->address = HackedZwCreateProcess;
        ( ( PASMJUMP )ZwCreateProcess )->jmp_eax = 0xE0FF;
    }
    else
    {
        /*
         * mov eax, HackedZwCreateProcessEx
         * jmp eax
         */
        ( ( PASMJUMP )ZwCreateProcessEx )->mov_eax = 0xB8;
        ( ( PASMJUMP )ZwCreateProcessEx )->address = HackedZwCreateProcessEx;
        ( ( PASMJUMP )ZwCreateProcessEx )->jmp_eax = 0xE0FF;
    }
    /*
     * BOOL CreateProcess
     * (
     *     LPCTSTR               lpApplicationName,    // name of executable module
     *     LPTSTR                lpCommandLine,        // command line string
     *     LPSECURITY_ATTRIBUTES lpProcessAttributes,  // SD
     *     LPSECURITY_ATTRIBUTES lpThreadAttributes,   // SD
     *     BOOL                  bInheritHandles,      // handle inheritance option
     *     DWORD                 dwCreationFlags,      // creation flags
     *     LPVOID                lpEnvironment,        // new environment block
     *     LPCTSTR               lpCurrentDirectory,   // current directory name
     *     LPSTARTUPINFO         lpStartupInfo,        // startup information
     *     LPPROCESS_INFORMATION lpProcessInformation  // process information
     * );
     */
    if ( FALSE == CreateProcess
                  (
                      NULL,
                      argv[1],
                      NULL,
                      NULL,
                      TRUE,
                      CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
                      NULL,
                      NULL,
                      &si,
                      &pi
                  ) )
    {
        PrintWin32Error( "CreateProcess() failed", GetLastError() );
        goto main_exit;
    }
    /*
     * CreateProcess()无论是否成功,都返回。与Unix下的exec*()不同。
     */

main_exit:

    if ( NULL != pi.hThread )
    {
        CloseHandle( pi.hThread );
        pi.hThread = NULL;
    }
    if ( NULL != pi.hProcess )
    {
        CloseHandle( pi.hProcess );
        pi.hProcess = NULL;
    }
    if ( NULL != winlogon )
    {
        CloseHandle( winlogon );
        winlogon = NULL;
    }
    DisableCurrentProcessDebugPrivilege();
    return( EXIT_FAILURE );
}  /* end of main */

/************************************************************************/

--------------------------------------------------------------------------

至于CreateRemoteThread(),想在这上面发彪的兄弟别搭理我,我不熟它,这次不尝
试。目的就是练手,以此慢慢熟悉某些咱感兴趣的Windows角落,功利主义者是也。

再谢rain、flier的指点以及sparrow的共享代码。

☆ 参考资源

[ 1] Strace for NT
     http://razor.bindview.com/tools/desc/strace_readme.html
     http://razor.bindview.com/tools/files/strace-0.3.zip

[ 2] LsaAddAccountRights
     http://www.mvps.org/win32/lsa/laar.html
     http://www.mvps.org/win32/lsa/lsa_laar.cpp

[ 3] GUI-Based RunAs
     http://www.codeguru.com/misc/RunUser.html

[ 4] Start a Command As Any User
     http://www.codeguru.com/misc/CmdAsUser.html

[ 5] <<Windows NT/2000 Native API Reference>> - Gary Nebbett
版权所有,未经许可,不得转载