Tutorials

Assigning Debug Privileges under Windows XP at run-time

Most of the interesting operations on Windows kernel, such as memory operations, in particular the Shadow Table, the correct address of which has been discussed earlier in one of my articles, require defining special OS security settings, often called Privileges. On Windows 2000/XP the lowest Privilege, required by memory management routine is certainly Debug Privilege.

Definitely, it is quite possible to set up the security privileges in Windows from the Administrative panel, by choosing "Programs\Administrative tools\Local Security Settings", but sometime it has to be done at run-time from within the code. This rather small article shall be devoted to the problem of setting up Debug Privileges under Windows XP at run-time.

First of all, the code, presented below is to be called from the user mode application, not from the kernel. However, the impact of the code execution will affect the entire system, and therefore the kernel as well.

Basically, the entire procedure of changing privileges comes down to calling two security functions in the right order. First LookupPrivilegeValue, then AdjustTokenPrivileges. Their prototypes are as follows:

BOOL LookupPrivilegeValue(
  LPCTSTR lpSystemName,
  LPCTSTR lpName,
  PLUID lpLuid
);
BOOL AdjustTokenPrivileges(
  HANDLE TokenHandle,
  BOOL DisableAllPrivileges,
  PTOKEN_PRIVILEGES NewState,
  DWORD BufferLength,
  PTOKEN_PRIVILEGES PreviousState,
  PDWORD ReturnLength
);

Due to operating system security, assigning privileges requires an access token handle. This token contains current system security information and may be used to control access to securable objects and the right of a user to perform various system related operations. Since the privilege adjustment is implemented within the code, it makes sense to obtain the access token for the current process initiated by this code. Function OpenProcessToken is used to return a correct handle to the access token on behalf of the given process, it has the following prototype:

BOOL OpenProcessToken(
  HANDLE ProcessHandle,
  DWORD DesiredAccess,
  PHANDLE TokenHandle
);

Finally, to store information on access privileges for the access token, an instance of TOKEN_PRIVILEGES structure has to be defined. This structure is presented below:

typedef struct _TOKEN_PRIVILEGES {
  DWORD PrivilegeCount; //Specifies the number of entries in the Privileges array
  LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; // Specifies an array of LUID_AND ATTRIBUTES structures
} TOKEN_PRIVILEGES, 
*PTOKEN_PRIVILEGES;

This, in turn, uses another structure, LUID_AND_ATTRIBUTES, worth noticing here:

typedef struct _LUID_AND_ATTRIBUTES {
  LUID Luid; //Specifies an Locally Unique ID (LUID) value
  DWORD Attributes; // Specifies attributes of the LUID
} LUID_AND_ATTRIBUTES, 
*PLUID_AND_ATTRIBUTES;

Now, when all the structures and functions are defined, they are just needed to be called in the right to set the privileges. This is a three steps problem, which requires every step to be successful in order for the entire code to succeed. Therefore, every step has to be checked for the valid output before proceeding to the next step.


Step 1. Obtaining current privileges status


First, an instance of TOKEN_PRIVILEGES structure has to be defined:

TOKEN_PRIVILEGES Debug_Privileges;

And then, call the LookupPrivilegeValue function with a pointer to that structure to get the required information. Check the return status of the function:

if (!LookupPrivilegeValue (NULL, // Privieleges for the local system
							SE_DEBUG_NAME, // define the name of the privilege 
                           &Debug_Privileges.Privileges[0].Luid)) // will get the LUID value into this variable
{	//if function failed, cannot proceed to the next step
	return GetLastError(); //terminate the outer function
}

Step 2. Obtaining current process Access Token


As soon as the LUID value is received, we can proceed to receiving an access token. The current process is used to get the valid token:

DWORD err = 0; // define error holder, used to store the error code in case of failure
HANDLE hToken = 0; //  instantiate a token handle 
if (!OpenProcessToken (GetCurrentProcess (), // current process ID handle
                       TOKEN_ADJUST_PRIVILEGES, //set the desired access
                       &hToken)) // handle to the token will be held here 
{	// if function failed, cannot proceed to the next step
	err = GetLastError();  
	if (hToken) // if handle is still valid
   	CloseHandle (hToken); // destroy it
	return err; //terminate the outer function
}

Step 3. Adjusting privileges on behalf of the current process


Before passing current privileges status Debug_Privileges into AdjustTokenPrivileges function, it has to be properly set:

Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // set to enable privilege
Debug_Privileges.PrivilegeCount = 1; // working with only one privilege

Now, the structure has to be passed over to AdjustTokenPrivileges:

if (!AdjustTokenPrivileges (hToken, // access token handle
							FALSE, // do not disable privileges
							&Debug_Privileges, // pointer to the token structure
							0,  // no need for a buffer 
							NULL, // previous state not set
							NULL)) //  no need for a buffer
{	if function failed, cannot proceed to the next step
	err = GetLastError();
	if (hToken) // if handle is still valid
        CloseHandle (hToken); // destroy it
	return err; //terminate the outer function
}

At this point, if everything went fine, the debug privileges should be properly assigned. The full code, wrapped into a convenient function is presented below:

int SetDebugPrivileges(void)
{
	TOKEN_PRIVILEGES Debug_Privileges;

	//STEP 1
	if (!LookupPrivilegeValue (NULL, // Privieleges for the local system
								SE_DEBUG_NAME, // define the name of the privilege 
                               &Debug_Privileges.Privileges[0].Luid)) // will get the LUID value into this variable
   {	//if function failed, cannot proceed to the next step
		return GetLastError(); //terminate the outer function
   }

	//STEP 2
	DWORD err = 0; // define error holder, used to store the error code in case of failure
	HANDLE hToken = 0; //  instantiate a token handle 
	if (!OpenProcessToken (GetCurrentProcess (), // current process ID handle
                           TOKEN_ADJUST_PRIVILEGES, //set the desired access
                           &hToken)) // handle to the token will be held here 
   {	// if function failed, cannot proceed to the next step
		err = GetLastError();  
		if (hToken) // if handle is still valid
        	CloseHandle (hToken); // destroy it
		return err; //terminate the outer function
   }

	//STEP3
	Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // set to enable privilege
	Debug_Privileges.PrivilegeCount = 1; // working with only one privilege

	if (!AdjustTokenPrivileges (hToken, // access token handle
								FALSE, // do not disable privileges
								&Debug_Privileges, // pointer to the token structure
								0,  // no need for a buffer 
								NULL, // previous state not set
								NULL)) //  no need for a buffer
   {
        err = GetLastError();
		if (hToken) // if handle is still valid
        CloseHandle (hToken); // destroy it
		return err; //terminate the outer function
   }

	return err; 
}

This function may be called in the following fashion:

if (SetDebugPrivileges() != 0)
	printf("failed");
else
	printf("success");
	
 
Alexander Volynkin ICQ #: 490622
Last update: 09.26.2004