This blog post explains using PowerShell bitwise comparison operators to check and set bit flags. You can also use PowerShell to calculate bit flags by creating your own enum if desired.
We must first start with a (really) basic primer on decimal values and their binary equivalent.
Take the decimal value of 86. If we plot this in a binary table we can see that the binary equivalent of the decimal 86 is 01010110 by checking all of the bits set to 1 (64 + 16 + 4 + 2). This is 8-bit binary since there are only 8 bits.
MSB | Bit | Bit | Bit | Bit | Bit | Bit | LSB |
---|---|---|---|---|---|---|---|
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
(MSB = most significant bit. LSB = least significant bit)
In the real-world, we use bits to represent MANY things. One of the most relatable examples is the ASCII table. Originally ASCII was 7-bits, meaning that it only allowed 128 different characters (64+32+16+8+4+2+1 = 127 characters plus all 0’s = 128 characters). But later this was extended to 8-bit binary, allowing 256 characters (128+64+32+16+8+4+2+1 = 255 characters plus all 0’s = 256 characters).
You can see in the link above that the decimal value of an upper-case K character is 75. Which we can represent in binary like so:
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 |
Another real-word example is colours! Think RGB colours (red/green/blue) such as 255/175/17. Each value represents an intensity of 8-bit colour, 0 being no colour and 255 being the most intense colour!
Now we have a brief understanding of decimal and binary and where they are used, we need to understand how we can check if bits are set and change them if required. And to do that, we must first understand some of the bitwise comparison operators. When comparing bits in binary strings we typically use AND, OR and XOR bitwise operators.
The Bitwise AND Operator
With the AND operator, if BOTH bit flags are 1 the result is a 1. Otherwise it’s a 0. For example:
(01001100 AND 11010110 = 01000100)
0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
The Bitwise OR Operator
With the OR operator, if either bit is 1 or both bits are 1 the result is a 1. Otherwise it’s a 0. For example:
(01001100 OR 11010110 = 11011110)
0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 |
The Bitwise XOR Operator
With the XOR operator if either bit is 1 and the other bit is 0, the result is a 1. But if both bits are 1 or both bits are 0, it’s a 0. For example:
(01001100 XOR 11010110 = 10011010)
0 | 1 | 0 | 0 | 1 | 1 | 0 | 0 |
1 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
Checking if Bits are Set using PowerShell
Let’s take our earlier example which represents the decimal value of 86:
128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
If we want to check if bit 16 is set to 1, we can simply write:
$decimalval = 86
$bitflag16 = 16
$result = ($decimalval -band $bitflag16)
if ($result -eq $bitflag16) {
write-host "Bit 16 is set"
} else {
write-host "Bit 16 is NOT set"
}
Essentially we are comparing the following binary values using an AND comparison.
0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
The output of the binary and operator is 0 if the bit is NOT set. If it is set, it will return the bit flag value of 16 in this case.
Setting Bits using PowerShell
We might want the ability to set specific bit flags using PowerShell. So let’s take a real-word example of wanting to disable/enable a computer object in Active Directory.
To enable or disable a computer object in Active Directory we must configure the bit flag of 2 (ACCOUNTDISABLE) to either 0 or 1 on the UserAccountControl attribute.
You will notice that there are a LOT of bit flags for UserAccountControl. The number of bit flags no longer fit into 8-bit binary like our examples above! It actually uses 26-bits instead! We can tell quite easily too: in our example above using 8-bits, we can check the size if we calculate 2 (binary is a base 2 number) to the power 8 (bits), which equals 256.
In this case, we can see that the decimal value goes up to 67108864! Which is 2 to the power 26! Meaning 26-bits! But it makes no difference to us since our logic remains the same.
We can see in the documentation that the bit flag 2 represents ACCOUNTDISABLE. If this is set to 1 the account is disabled, and if it is set to 0 the account is enabled.
Let’s assume the UserAccountControl decimal value for a computer object we retrieve from Active Directory is 4096 to begin with, meaning that the ACCOUNTDISABLE flag is set to 0 (the account is enabled).
We can verify this and check if bit flag 2 is set like so:
$decimalval = 4096
$bitflag2 = 2
$result = ($decimalval -band $bitflag2)
if ($result -eq $bitflag2) {
write-host "Bit $bitflag2 is set."
} else {
write-host "Bit $bitflag2 is NOT set"
}
If we wanted to see the binary equivalent of the decimal 4096 to do a manual check of the bit flag, we can write it like so:
write-host ([Convert]::ToString(4096,2))
Which equals (in binary): 1000000000000. In other words, the only flag that is set is for WORKSTATION_TRUST_ACCOUNT.
To set the ACCOUNTDISABLE flag, we want to create a bit mask representing that flag (decimal value of 2) and then when we compare both values, we can use the binary OR comparison in PowerShell to say “If either value is set to 1, our new value should be 1” like so:
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
$decimalval = 4096
$bitflag2 = 2
$newdecimalval = ($decimalval -bor $bitflag2)
write-host $newdecimalval
The new binary value now equals: 1000000000010. If we wanted to unset the flag and revert to a binary value of 1000000000000, we would need to use the XOR binary comparison operator. In other words, “if both values are a 1 or both values are a zero, set the flag to be 0”.
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
$decimalval = 4098
$bitflag2 = 2
$newdecimalval = ($decimalval -bxor $bitflag2)
write-host $newdecimalval
And when we tie it all together (try changing 4096 to 4098 back and forth to see the flag toggle):
$decimalval = 4096
$bitflag2 = 2
$result = ($decimalval -band $bitflag2)
if ($result -eq $bitflag2) {
write-host "Bit $bitflag2 is set. Unset it."
$newdecimalval = ($decimalval -bxor $bitflag2)
write-host $newdecimalval
} else {
write-host "Bit $bitflag2 is NOT set"
$newdecimalval = ($decimalval -bor $bitflag2)
write-host $newdecimalval
}
And if we really wanted to tie it into Active Directory to use PowerShell to enable and disable computers, we can use the following example:
#only computers
$computerName = "AlkaneComputerName"
$objSearcher=[adsisearcher]"(&(objectCategory=computer)(Name=$computerName))"
$result = $objSearcher.FindOne()
if ($result -ne $null)
{
$comp = [adsi]($result.Properties).adspath[0]
$assetname = ($result.Properties).name
write-host "Processing $assetname"
$decimalval = $comp.userAccountControl[0]
$bitflag2 = 2
$result = ($decimalval -band $bitflag2)
if ($result -eq $bitflag2) {
write-host "$assetname is disabled."
} else {
write-host "$assetname is enabled. Disabling."
$newdecimalval = ($decimalval -bor $bitflag2)
$comp.put("userAccountControl", $newdecimalval)
$comp.setinfo()
}
}