I have an issue not able to add all Sid's to my current loop. Everything else is working as I expected. I just need help adding in my code to add the SID for each user my code is displaying. The SID will now show.
new error message:
Here is my current code:
namespace ActiveDirectoryDisplayNamesApp
{
class Program
{
static void Main(string[] args)
{
using (var context = new PrincipalContext(ContextType.Domain, "nor-amcoldcorp.local"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
var sidByte = ObjectToByteArray(de.Properties["objectSId"].Value);
Console.WriteLine("First Name: " + de.Properties["givenName"].Value);
Console.WriteLine("Last Name : " + de.Properties["sn"].Value);
Console.WriteLine("SAM account name : " + de.Properties["samAccountName"].Value);
Console.WriteLine("User principal name: " + de.Properties["userPrincipalName"].Value);
Console.WriteLine("Object Sid: " + System.Text.Encoding.UTF8.GetString(sidByte)); //Here is the changement
Console.WriteLine();
}
}
}
Console.ReadLine();
}
static public byte[] ObjectToByteArray(Object obj)
{
if (obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
}
de.Properties["objectSid"].value returns a Byte [] array, to view the SID you will need to parse this into a string to get the functionality you are looking for. A good post on how to do that can be found here.
Below is the function you will need to convert the array into a usable string:
public static string ConvertByteToStringSid(Byte[] sidBytes)
{
StringBuilder strSid = new StringBuilder();
strSid.Append("S-");
try
{
// Add SID revision.
strSid.Append(sidBytes[0].ToString());
// Next six bytes are SID authority value.
if (sidBytes[6] != 0 || sidBytes[5] != 0)
{
string strAuth = String.Format
("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
(Int16)sidBytes[1],
(Int16)sidBytes[2],
(Int16)sidBytes[3],
(Int16)sidBytes[4],
(Int16)sidBytes[5],
(Int16)sidBytes[6]);
strSid.Append("-");
strSid.Append(strAuth);
}
else
{
Int64 iVal = (Int32)(sidBytes[1]) +
(Int32)(sidBytes[2] << 8) +
(Int32)(sidBytes[3] << 16) +
(Int32)(sidBytes[4] << 24);
strSid.Append("-");
strSid.Append(iVal.ToString());
// Get sub authority count...
int iSubCount = Convert.ToInt32(sidBytes[7]);
int idxAuth = 0;
for (int i = 0; i < iSubCount; i++)
{
idxAuth = 8 + i * 4;
UInt32 iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
strSid.Append("-");
strSid.Append(iSubAuth.ToString());
}
}
catch (Exception ex)
{
}
return strSid.ToString();
}
And here is what you will need to call the function:
System.DirectoryServices.PropertyCollection coll = de.Properties;
object obVal = coll["objectSid"].Value;
string yourSID;
if (null != obVal)
{
yourSID = ConvertByteToStringSid((Byte[])obVal);
}
EDIT :
Declare this function :
private byte[] ObjectToByteArray(Object obj)
{
if(obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
Than do this :
byte[] bytes = Encoding.Default.GetBytes(de.Properties["objectSid"].value);
sidByte= Encoding.UTF8.GetString(bytes);
Console.WriteLine("Object Sid: " + sidByte ); //Here is the changement
Or you try this approcah (if the first one didn't work) but keep the first function that will convert your byte object to byte :
Declare a function
static string BytesToStringConverted(byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd();
}
}
}
Than call it like that :
Console.WriteLine("Object Sid: " +BytesToStringConverted (sidByte)
There is already a dedicated Sid class that you can use for decoding Sid data.
var sidBytes = (byte[])de.Properties["objectSId"].Value;
var sid = new SecurityIdentifier(sidBytes ,0);
string strSid = sid.Value;//Something like S-1-5-21..
.
For hexadecimal form, it should be: string strAuth = String.Format("0x{0:x2}{1:x2}{2:x2}{3:x2}{4:x2}{5:x2}",
For .NET 5, SecurityIdentifier only available on Windows.
https://github.com/dotnet/runtime/blob/6bc6560e51d1cf58b54561f7be44801864479b8d/src/libraries/System.Security.Principal.Windows/src/System/Security/Principal/SID.cs#L403-L478
Found few bugs from the original
https://www.codeproject.com/articles/3688/how-to-get-user-sid-using-directoryservices-classe
public static string ConvertByteToStringSid(Byte[] sidBytes)
hexadecimal conversion (Wrong "0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}", right: "0x{0:x2}{1:x2}{2:x2}{3:x2}{4:x2}{5:x2}")
Sub Authority Count at the second byte, not the 8th byte (Wrong Convert.ToInt32(sidBytes[7]), right: Convert.ToInt32(sidBytes[1]))
I have followed the original implementation and compare with the result from class SecurityIdentifier, also consider the comment:
SID decoding is wrong Pin
Hi there,
I think your SID decoding is wrong. The number of sub authorities is the 2nd byte in the SID byte array, not the 8th, and the main authority has its bytes stored in the other order from the one you're reading in.
I'd like to share a better version, can be found at:
https://gist.github.com/thohng/8820153f7d1e107b6619b34fd765f887:
public static string ConvertByteToStringSid(byte[] sidBytes)
{
if (sidBytes == null || sidBytes.Length < 8 ||
sidBytes.Length > 68) // maximum 15 sub authorities
return string.Empty;
var span = new ReadOnlySpan<byte>(sidBytes);
var strSid = new StringBuilder("S-");
// Add SID revision.
strSid.Append(span[0]);
// Get sub authority count...
var subAuthoritiesLength = Convert.ToInt32(span[1]);
if (sidBytes.Length != 8 + subAuthoritiesLength * 4)
return string.Empty;
long identifierAuthority =
(((long)span[2]) << 40) +
(((long)span[3]) << 32) +
(((long)span[4]) << 24) +
(((long)span[5]) << 16) +
(((long)span[6]) << 8) +
span[7];
strSid.Append('-');
strSid.Append(identifierAuthority);
span = span[8..];
for (int i = 0; i < subAuthoritiesLength; i++, span = span[4..])
{
strSid.Append('-');
strSid.Append(BitConverter.ToUInt32(span.Slice(0, 4)));
}
return strSid.ToString();
}
And unit tests:
private static Func<byte[], string> GetConvertByteToStringSidService() => LdapHelper.ConvertByteToStringSid;
[Fact]
public void ConvertByteToStringSid_Builtin()
{
var service = GetConvertByteToStringSidService();
var sid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 39, 2, 0, 0 };
var result = service(sid);
Assert.Equal("S-1-5-32-551", result);
}
[Fact]
[SupportedOSPlatform("windows")]
public void ConvertByteToStringSid_Builtin_Windows()
{
var sid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 39, 2, 0, 0 };
var s2 = new SecurityIdentifier(sid, 0);
Assert.Equal("S-1-5-32-551", s2.ToString());
}
[Fact]
public void ConvertByteToStringSid_Malformed()
{
var service = GetConvertByteToStringSidService();
var sid1 = new byte[] { 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0, 1 };
var result1 = service(sid1);
Assert.Equal("", result1);
var sid2 = new byte[] { 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0 };
var result2 = service(sid2);
Assert.Equal("", result2);
var sid3 = new byte[] { 1, 4, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0 };
var result3 = service(sid3);
Assert.Equal("", result3);
}
[Fact]
public void ConvertByteToStringSid_Max()
{
var service = GetConvertByteToStringSidService();
var sid = new byte[] { 1, 1, 255, 254, 253, 252, 0, 0, 251, 250, 249, 248 };
var result = service(sid);
Assert.Equal("S-1-281470647926784-4177132283", result);
var sid2 = new byte[] { 1, 5, 136, 0, 44, 89, 0xFE, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0 };
var result2 = service(sid2);
Assert.Equal("S-1-149534325472773-21-71093982-54227939-39018152-1025", result2);
}
[Fact]
[SupportedOSPlatform("windows")]
public void ConvertByteToStringSid_Max_Windows()
{
var sid = new byte[] { 1, 1, 255, 254, 253, 252, 0, 0, 251, 250, 249, 248 };
var s1 = new SecurityIdentifier(sid, 0);
Assert.Equal("S-1-281470647926784-4177132283", s1.ToString());
var sid2 = new byte[] { 1, 5, 136, 0, 44, 89, 0xFE, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0 };
var s2 = new SecurityIdentifier(sid2, 0);
Assert.Equal("S-1-149534325472773-21-71093982-54227939-39018152-1025", s2.ToString());
}
[Fact]
public void ConvertByteToStringSid_NullEmpty()
{
var service = GetConvertByteToStringSidService();
var sid1 = Array.Empty<byte>();
var result1 = service(sid1);
Assert.Equal("", result1);
var result2 = service(null);
Assert.Equal("", result2);
}
[Fact]
public void ConvertByteToStringSid_Success()
{
var service = GetConvertByteToStringSidService();
var sid = new byte[] { 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0 };
var result = service(sid);
Assert.Equal("S-1-5-21-71093982-54227939-39018152-1025", result);
}
[Fact]
[SupportedOSPlatform("windows")]
public void ConvertByteToStringSid_Windows()
{
var sid = new byte[] { 1, 5, 0, 0, 0, 0, 0, 5, 21, 0, 0, 0, 222, 206, 60, 4, 227, 115, 59, 3, 168, 94, 83, 2, 1, 4, 0, 0 };
var s1 = new SecurityIdentifier(sid, 0);
Assert.Equal("S-1-5-21-71093982-54227939-39018152-1025", s1.ToString());
}
So I need to cut off the first 16 bytes from my byte array. I followed another post I saw on Stack Overflow to use the following code:
//split message into iv and encrypted bytes
byte[] iv = new byte[16];
byte[] workingHash = new byte[rage.Length - 16];
//put first 16 bytes into iv
for (int i = 0; i < 16; i++)
{
iv[i] = rage[i];
}
Buffer.BlockCopy(rage, 16, workingHash, 0, rage.Length);
What we are trying here is to cut off the first 16 bytes from the byte[] rage and put the rest into byte[] workingHash
The error occurs at Buffer.BlockCopy(rage, 16, workingHash, 0, rage.Length);
Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.
Any help will be much appreciated.
The problem is trivial: Buffer.BlockCopy's last argument requires the correct number of bytes to be copied, which (taking the starting index into account) may not exceed the array's bounds (docs).
Hence the code should look like this, avoiding any for cycles:
Buffer.BlockCopy(rage, 0, iv, 0, 16);
Buffer.BlockCopy(rage, 16, workingHash, 0, rage.Length - 16);
Notice the “- 16” at the second line, fixing the original code. The first line replaces the for cycle for the sake of consistency.
Lets assume rage is a byte array of length 20:
var rage = new byte[20]
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
};
After byte[] iv = new byte[16];, iv will contain:
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
After byte[] workingHash = new byte[rage.Length - 16];, workingHash will contain:
{ 0, 0, 0, 0 }
After the for loop iv is:
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }
You need:
Buffer.BlockCopy(rage, 16, workingHash, 0, rage.Length - 16);
Copy rage.Length - 16 (4) elements from rage's 16th element (which is 17) to workingHash starting from the 0th element.
The result:
{ 17, 18, 19, 20 }
By the way there is a very readable way, probably not as fast as copying arrays, but worth mentioning:
var firstSixteenElements = rage.Take(16).ToArray();
var remainingElements = rage.Skip(16).ToArray();
Fixed:
//split message into iv and encrypted bytes
byte[] iv = new byte[16];
byte[] workingHash = new byte[rage.Length - 16];
//put first 16 bytes into iv
for (int i = 0; i < 16; i++)
{
iv[i] = rage[i];
}
for (int i = 0; i < rage.Length - 16; i++)
{
workingHash[i] = rage[i + 16];
}
I am trying to write a C# program similar to the one on this website: http://www.digital-detective.co.uk/freetools/decode.asp
Can you please tell me how I can convert the hex numbers listed in the following bulletins to Date/Time values.
Windows 64 bit (little endian) hex value FF03D2315FE1C701 should
converts to = Sat, 18 August 2007 06:15:37 UTC
Windows 64 bit OLE hex value FBE8DF975D3FE340 should converts to =
Sun, 02 December 2007 22:11:42 UTC
Unix 32 bit (big endian) hex value 46C3B400 should converts to =
Thu, 16 August 2007 02:18:40 UTC
Apple Mac Absolute hex value 219216022 should converts to = Thu,
13 December 2007 05:20:22 UTC
HFS 32 bit (little endian) hex value CD4E55C3 should converts to =
Mon, 05 November 2007 22:50:53 Local
I was trying to use the following code to do that, but it doesn't return the correct result:
double decValue = int.Parse("A2C3B446", System.Globalization.NumberStyles.HexNumber);
System.DateTime dtDateTime = new DateTime(2013, 1, 1, 0, 0, 0, 0);
dtDateTime = dtDateTime.AddSeconds(decValue).ToLocalTime();
Console.WriteLine("Decimal Value: " + decValue);
Console.WriteLine(dtDateTime);
You'll first want a utility method that reads bytes from a stream and handles the endian-ness. That could look like:
public static byte[] ReadBytes(Stream s, int size, bool littleEndian) {
var bytes = new byte[size];
var len = s.Read(bytes, 0, size);
if (len != size) throw new InvalidOperationException("Unexpected end of file");
if (BitConverter.IsLittleEndian != littleEndian) Array.Reverse(bytes);
return bytes;
}
Windows dates are easy, supported by DateTime.FromFileTimeUtc() directly:
public static DateTime ConvertWindowsDate(byte[] bytes) {
if (bytes.Length != 8) throw new ArgumentException();
return DateTime.FromFileTimeUtc(BitConverter.ToInt64(bytes, 0));
}
Testing it with your value:
var date1 = DateReaders.ConvertWindowsDate(DateReaders.ReadBytes(
new MemoryStream(new byte[]{0xFF,0x03,0xD2,0x31,0x5F,0xE1,0xC7,0x01}), 8, true));
Produces {8/18/2007 6:15:37 AM} as expected.
OLE dates are easy, supported by DateTime.FromOADate() directly:
public static DateTime ConvertOLEDate(byte[] bytes) {
if (bytes.Length != 8) throw new ArgumentException();
return DateTime.FromOADate(BitConverter.ToDouble(bytes, 0));
}
Testing it with your value:
var date2 = DateReaders.ConvertOLEDate(DateReaders.ReadBytes(
new MemoryStream(new byte[] {0xFB,0xE8,0xDF,0x97,0x5D,0x3F,0xE3,0x40 }), 8, true));
Produces {12/2/2007 10:11:41 PM}
Unix date values are milliseconds from Jan 1st, 1970, 0:00 AM UTC:
public static DateTime ConvertUnixDate(byte[] bytes) {
if (bytes.Length != 4) throw new ArgumentException();
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(
BitConverter.ToUInt32(bytes, 0));
}
Testing it with your value:
var date3 = DateReaders.ConvertUnixDate(DateReaders.ReadBytes(
new MemoryStream(new byte[] {0x46,0xC3,0xB4,0x00}), 4, false));
Produces {8/16/2007 2:18:40 AM}
Apple Mac absolute time is documented to be CPU dependent and requires conversion on the machine that generated it. The shown value "219216022" is quirky, it appears to decimal instead of hex like all the other ones. I'll follow Baldrick's lead with:
public static DateTime ConvertAppleDate(byte[] bytes) {
if (bytes.Length != 4) throw new ArgumentException();
return new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(
BitConverter.ToUInt32(bytes, 0));
}
HFS dates are seconds since Jan 1st, 1904, 0:00 AM. Do note that HFS dates are local time but HFS Plus dates are UTC. I'll assume local since that's the result you documented:
public static DateTime ConvertHFSDate(byte[] bytes) {
if (bytes.Length != 4) throw new ArgumentException();
return new DateTime(1904, 1, 1, 0, 0, 0, DateTimeKind.Local).AddSeconds(
BitConverter.ToUInt32(bytes, 0));
}
Testing it with your value:
var date5 = DateReaders.ConvertHFSDate(DateReaders.ReadBytes(
new MemoryStream(new byte[] {0xCD,0x4E,0x55,0xC3 }), 4, true));
Produces {11/5/2007 10:50:53 PM}