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;
LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES,
*PTOKEN_PRIVILEGES;
This, in turn, uses another structure, LUID_AND_ATTRIBUTES, worth noticing here:
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} 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,
SE_DEBUG_NAME,
&Debug_Privileges.Privileges[0].Luid))
{
return GetLastError();
}
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;
HANDLE hToken = 0;
if (!OpenProcessToken (GetCurrentProcess (),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
err = GetLastError();
if (hToken)
CloseHandle (hToken);
return err;
}
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;
Debug_Privileges.PrivilegeCount = 1;
Now, the structure has to be passed over to AdjustTokenPrivileges:
if (!AdjustTokenPrivileges (hToken,
FALSE,
&Debug_Privileges,
0,
NULL,
NULL))
{
err = GetLastError();
if (hToken)
CloseHandle (hToken);
return err;
}
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;
if (!LookupPrivilegeValue (NULL,
SE_DEBUG_NAME,
&Debug_Privileges.Privileges[0].Luid))
{
return GetLastError();
}
DWORD err = 0;
HANDLE hToken = 0;
if (!OpenProcessToken (GetCurrentProcess (),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
err = GetLastError();
if (hToken)
CloseHandle (hToken);
return err;
}
Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
Debug_Privileges.PrivilegeCount = 1;
if (!AdjustTokenPrivileges (hToken,
FALSE,
&Debug_Privileges,
0,
NULL,
NULL))
{
err = GetLastError();
if (hToken)
CloseHandle (hToken);
return err;
}
return err;
}
This function may be called in the following
fashion:
if (SetDebugPrivileges() != 0)
printf("failed");
else
printf("success");