1. 程式人生 > >轉貼 Administrator使用者直接獲取SYSTEM許可權

轉貼 Administrator使用者直接獲取SYSTEM許可權

               

 Administrator使用者直接獲取SYSTEM許可權


來源:http://www.nsfocus.com
作者:"scz" <[email protected]>


標題: MSDN系列(3)--Administrator使用者直接獲取SYSTEM許可權

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

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

目錄:

  ☆ 概述
  ☆ sysproc.c
  ☆ sysproc_now.c
  ☆ 參考資源

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

☆ 概述

翻看[5]的時候,其書中例8.1演示如何以SYSTEM身份啟動cmd。以前我為了達到同樣
目的,利用at命令,但平日裡禁用Task Scheduler Service的,感覺操作很繞彎,不
爽得很。網上有現成的可執行程式直接獲取SYSTEM許可權,不喜歡沒原始碼的此型別小工
具,從未試用過。

麻雀兄 <[email protected]>在水木清華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 <[email protected]>
*
* Add support for Windows XP/2003 by scz <[email protected]>
* 2003-06-21 21:22
*/

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