关闭

Administrator用户直接获取SYSTEM权限

标签: systemattributestokennullstatisticsstruct
4850人阅读 评论(0) 收藏 举报
分类:
来源:http://www.nsfocus.com
作者:"scz" <scz@nsfocus.com>


标题: 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

 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:3763386次
    • 积分:60902
    • 等级:
    • 排名:第43名
    • 原创:1549篇
    • 转载:1252篇
    • 译文:0篇
    • 评论:459条
    最新评论