Spelunking Windows – Tokens for fun and profitPosted: May 22, 2013
I want to shutdown/restart my machine programmatically. There’s an API for that:
-- kernel32.dll BOOL InitiateSystemShutdownExW( LPWSTR lpMachineName, LPWSTR lpMessage, DWORD dwTimeout, BOOL bForceAppsClosed, BOOL bRebootAfterShutdown, DWORD dwReason);
Wow, it’s that easy?!!
OK. So, I need the name of the machine, some message to display in a dialog box, a timeout, force app closure, reboot or not, and some reason why the shutdown is occuring. That sounds easy enough. So, I’ll just give it a call…
local status = core_shutdown.InitiateSystemShutdownExW( nil, -- nil, so local machine nil, -- no special message 10, -- wait 10 seconds false, -- don't force apps to close true, -- reboot after shutdown ffi.C.SHTDN_REASON_MAJOR_APPLICATION);
And what do I get for my troubles?
> error: (5) ERROR_ACCESS_DENIED
Darn, now I’m going to have to read the documentation.
In the Remarks of the documentation, it plainly states:
To shut down the local computer, the calling thread must have the SE_SHUTDOWN_NAME privilege.
Yah, ok, right then. What’s a privilege? And thus Alice went down into the rabbit’s hole…
As it turns out, there are quite a few concepts in Windows that are related to identity, security, authorization, and the like. As soon as you log into your machine, even if done programmatically, you get this thing called a ‘Token’ attached to your process. The easiest way to think of the token is it’s your electronic proxy and passport. Just like your passport, this token contains some basic identity information about who you are (name, identifying marks…). Some things in the system, such as being able to access a file, can be handled simply by knowing your name. These are simple access rights. But, other things in the system require a ‘visa’, meaning, not only does the operation have to know who you are, but it also needs to know you have the proper permissions to perform the operation you’re about to perform. It’s just like getting a visa stamped into your passport. If I want to travel to India, my passport alone is not enough. I need to get a visa as well. The same is true of this token thing. It’s not enough that I simply have an identity, I must also have a “privilege” in order to perform certain operations.
In addition to having a privilege, I must actually ‘activate’ it. So, yes, the system may have granted me the privilege, but it’s like super powers, you don’t want them to always be active. It’s like when you’re walking down the street in that foreign country you’re visiting. You don’t walk down the street flashing your fancy passport showing everyone the neat visas you have stamped in there. If you do, you’ll likely get a crowd following you trying to relieve you of said passport. So, you generally keep it to yourself, and only flash it when the need arises. So too with token privilege. Yes, you might have the ability to reboot the machine, but you don’t always want to have that privilege enabled, in case some nefarious software so happens to come along to exploit that fact.
Alright, that’s enough analogizing. How about some code. Well, it can be daunting to get your head around the various APIs associated with tokens. To begin with, there is a token associated with the process you’re currently running in, and there is a token associated with every thread you may launch from within that process as well. Generally, you want the process token if you’re single threaded. That’s one API call:
BOOL OpenProcessToken ( HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle );
This is one of those standard API calls where you pass in a couple of parameters (ProcessHandle, DesiredAccess), and a ‘handle’ is returned (TokenHandle). You then use the ‘handle’ to make subsequent calls to the various API functions. This is ripe for wrapping up in some nice data structure to deal with it.
I’ve created the ‘Token’ object, as the convenience point. One of the functions in there is this one:
getProcessToken = function(self, DesiredAccess) DesiredAccess = DesiredAccess or ffi.C.TOKEN_QUERY; local ProcessHandle = core_process.GetCurrentProcess(); local pTokenHandle = ffi.new("HANDLE ") local status = core_process.OpenProcessToken (ProcessHandle, DesiredAccess, pTokenHandle); if status == 0 then return false, errorhandling.GetLastError(); end return Token(pTokenHandle); end
One of the important things to take note of when you create a token is the DesiredAccess. What you can do with a token after it is created is somewhat determined by the access that you put into it when you create it. Here are the various options available:
static const int TOKEN_ASSIGN_PRIMARY =(0x0001); static const int TOKEN_DUPLICATE =(0x0002); static const int TOKEN_IMPERSONATE =(0x0004); static const int TOKEN_QUERY =(0x0008); static const int TOKEN_QUERY_SOURCE =(0x0010); static const int TOKEN_ADJUST_PRIVILEGES =(0x0020); static const int TOKEN_ADJUST_GROUPS =(0x0040); static const int TOKEN_ADJUST_DEFAULT =(0x0080); static const int TOKEN_ADJUST_SESSIONID =(0x0100);
For the case where we want to turn on a privilege that’s attached to the token, we will want to make sure the ‘TOKEN_ADJUST_PRIVILEGES’ access right is attached. It also does not hurt to add the ‘TOKEN_QUERY’ access as well. It’s probably best to use the least of these rights as is necessary to get the job done.
Setting a privilege on a token is another bit of work. It’s not hard, but it’s just one of those things where you have to read the docs, and look at a few samples on the internet in order to get it right. Assuming your token has the TOKEN_ADJUST_PRIVILEGES access right on it, you can do the following:
Token.enablePrivilege = function(self, privilege) local lpLuid, err = self:getLocalPrivilege(privilege); if not lpLuid then return false, err; end local tkp = ffi.new("TOKEN_PRIVILEGES"); tkp.PrivilegeCount = 1; tkp.Privileges.Luid = lpLuid; tkp.Privileges.Attributes = ffi.C.SE_PRIVILEGE_ENABLED; local status = security_base.AdjustTokenPrivileges(self.Handle.Handle, false, tkp, 0, nil, nil); if status == 0 then return false, errorhandling.GetLastError(); end return true; end
Well, that gets into some data structures, and introduces this thing called a LUID, and that AdjustTokenPrivileges function, and… I get tired just thinking about it. Luckily, once you have this function, it’s a fairly easy task to turn a privilege on and off.
OK. So, with this little bit of code in hand, I can now do the following:
local token = Token:getProcessToken(ffi.C.TOKEN_ADJUST_PRIVILEGES); token:enablePrivilege(Token.Privileges.SE_SHUTDOWN_NAME);
This just gets a token that is associated with the current process and turns on the privilege that allows us to successfully call the shutdown function.
-- test_shutdown.lua local ffi = require("ffi"); local core_shutdown = require("core_shutdown_l1_1_0"); local errorhandling = require("core_errorhandling_l1_1_1"); local Token = require("Token"); local function test_Shutdown() local token = Token:getProcessToken(); token:enablePrivilege(Token.Privileges.SE_SHUTDOWN_NAME); local status = core_shutdown.InitiateSystemShutdownExW(nil, nil, 10,false,true,ffi.C.SHTDN_REASON_MAJOR_APPLICATION); if status == 0 then return false, errorhandling.GetLastError(); end return true; end print(test_Shutdown());
And finally we emerge back into the light! This will now actually work. It’s funny, when I got this to work correctly, I pointed out to my wife that my machine was rebooting without me touching it. She tried to muster a smile of support, but really, she wasn’t that impressed. But, knowing the amount of work that goes into such a simple task, I gave myself a pat on the back, and smiled inwardly at the greatness of my programming fu.
Tokens are a very powerful thing in Windows. Being able to master both the concepts, and the API calls themselves, gives you a lot of control over what happens with your machine.