Under the hood of the ATI Ace Event Log

Or, Why is the event log maxing out the log with useless user information.

Anyone that has the Catalyst Control Center may have noticed the Ace Event Log.

Mine seems to stay full of errors and if you clear the log it will fill up again with just normal system use and not even opening the Catalyst Control Center to make any changes or to run any test. 

You can find this log by opening up the event viewer and then open the the application and services log and then you will see the Ace Event log.

Lets start with the test system specs.

Microsoft® Windows Vista™ Ultimate
Service Pack 2

AMD Phenom(tm) II X4 945 Processor
AMD64 Family 16 Model 4 Stepping 2    Revision 1026
64 bit Quad Core (Socket AM3) (125 watt)

ASUS M4A79T Deluxe Last available Bios Update(BIOS Date: 03/12/10 10:11:53 Ver: 08.00.15)

XFX HD-489X-ZSFC Radeon HD 4890 1GB 256-bit GDDR5 Pci Express 2.0 x16

Part Number: CM3X2048-1333C9 8GB (4 x 2 gig modules Paired)
Type:DDR3-SDRAM PC3-10700 (667MHz)  –  [DDR3-1333]

DirectX 11

Driver Packaging Version
Catalyst Version    13.1
Provider    Advanced Micro Devices, Inc.
2D Driver Version
2D Driver File Path    /REGISTRY/MACHINE/SYSTEM/ControlSet001/Control/CLASS/{4D36E968-E325-11CE-BFC1-08002BE10318}/0000
Direct3D Version
OpenGL Version
AMD VISION Engine Control Center Version    2012.1116.1515.27190

NOTE: On AMD CPU based systems it is now called “AMD Vision Engine Control Center” and on Intel CPU based systems it it is still called “Catalyst Control Center”


The Control Center is a module based system where the functionality is spread out among multiple modules.

Most of the modules are located in “C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static” and its sub folders. On my system Explorer says there are 341 files and 38 folder in the directory some folders are just language specific . That’s allot of files to keep track of and update for each new release.

If we take a look using Sysinternals Process Explorer We see that it starts with Mom.exe which in turn launches CCC.exe.


We can also see in the properties that these two are written in .Net clr version 2.0.50727

Using Process Explorer or a program I made to view the loaded modules we see that Mom.exe list 54 modules including, itself, the ATI modules and the .Net and system files required. Looking at CCC.exe it has 229 and over half of those are ATI modules.


Now lets get a look at the Ace Event Log.


One of the first things you notice is there are 731 events and they all have an event ID of “0” zero. Now that’s real helpful.

Another thing you may notice is , by the time stamps that they hit in groups.

So lets dig into a few of these and see if we can see why they are logged.


This is the error returned:
_IDEMDeviceDFP2Settings_0812.GetDFP2ITCFlag failed with status 2

By the function:

In order to dig in and see what is going on here we need to decompile the the modules and follow from module to module to get all of the information.

Here we will use a program called IlSpy which you can find here http://ilspy.net/ .

You can view the code and IL, C#, or VB.Net although some functions crashed the program while trying to view as VB.Net. I ended up staying in C# most of the time.

So how do we find this thing. Lets start by breaking down the function name.


ATI.ACE. = the folder name that the file is in (C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static) the Core-Static holds the bulk of these modules.

Next look in that folder for the file named ( CLI.Aspect.DeviceDFP.Graphics.Runtime )

Personally I copy the files I’m working with to a separate drive and folder and work with the copies.

Now we open this file with IlSpy and look for the class RT_DeviceDFP and then the function PrivateRefresh . Here is what we see.


The error shown here is not what was returned so lets dig deeper into the get method.

The get method traces down to the

// ATI.ACE.CLI.Aspect.DeviceDFP.Graphics.Runtime.RT_DeviceDFP
private void Parse(bool refreshDeviceInfo)


If we follow the get function we see:

// ATI.ACE.DEM.Graphics.IDEMDeviceDFP2Settings_0812
int GetDFP2ITCFlag(int demAdapterIndex, int displayIndex, ref bool itcFlagSupport, ref int itcFlag, ref int itcFlagDefault);

So it takes the parameters as input and somehow returns an integer value, perhaps it is adding the values or counting the ones that are not “0” zero. Without stepping thru this in a debugger it is difficult to know what the values are for the index’s.

The result was 2 in the error.

Looking at the code unless the index values are “0” zero then it will always throw this error because the other values are set to “0”, Boolean False = “0” .

This took almost a full day to sift thru the code and follow it thru multiple modules.

Next lets try an easier one (hopefully).



This one appears to be trying to subscribe to the registry key change event.

Looking thru the code on this one there are several problems with it.


First of all the path returned in the error, “HKCU;Software\wow6432node\ATI\ACE\Settings\AEM” does not exist on this system, this alone will cause it to fail every time.

For what ever reason they are using “;” instead of “\” in the output of the error message, perhaps to use a split Char later.  Also subscribe is misspelled in the error message.  (oops).

This function calls to the Windows API to open the registry key and does not appear to be checking for fails from those calls.

Those long numbers convert to the values of the default registry keys.



At this point I’m not sure where the sub key name came from so this is as far as I can go with this one.

Doing a string search it may be coming from:
C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static\CLIStart.exe

CLIStart.exe is a 32 bit unmanaged application so may be getting messed up with registry virtualization on a 64 bit system.


Lets try one more and we will call it a day.



This is the easiest one of all the file “ CLI.AIB.TutorialInfoCentre.Tutorial.Dashboard” just plain does not exist on this system. Thus the error that it can not be loaded.

I was not able to track down what tried to launch it to start with.


In conclusion this code base is so large and complicated that it is riddled with errors and it is also possible that due to the size of the code base and the registry keys that a previous version was not completely uninstalled and merged with the new version and is causing the problems. It would take a very long time to even attempt to track down the source of all of these errors.

If you don’t need the AMD/ATI user interface for overclocking  you could always uninstall it and just use the video drivers without the Catalyst Control Center.

I may test that next, after a normal uninstall and reboot see what is left and then cleaning all of the extra files and registry keys off the system .

That’s all for now I hope I was able to pass on some information oh how to dig into these although it can be difficult solve without being able to see the values in a debugger to solve some of them. There is a good chance you can not solve these without upgrading to a new version.

This version I’m using is already listed as legacy product. and it appears that there is another newer update since I last checked.


After uninstalling the old version, MSI crash and several reboots later and clearing the log file , there were still several files in the  windows\system32 folder.

Upon installing the new version with in and hour there were already over 230 new entry’s.

Go figure.

Posted in RootAdmin | Tagged | 2 Comments


Rootkits have become the most devious method of hiding malware on a system.

They are being employed to every sector from the home user to government to private industry.The monetary and information losses along with infrastructure disruption will continue to rise unless some way is developed to stop them.

What is a Rootkit ?

According to Wikipedia.

“A rootkit is a stealthy type of software, typically malicious, designed to hide the existence of certain processes or programs from normal methods of detection and enable continued privileged access to a computer.”

How do we stop them?

To stop them we have to understand how they work.

They, depending on how sophisticated they are can install a Windows service, Windows user mode driver, or a Windows kernel mode driver. They can also Infect the boot sector of a disk(boot kit), they can also create a hidden partition or volume to hide there files or even encrypt files so they are not easily identified about what they contain. They can also create task , either normal or WMI task to start at a given time or as a result to some action.I almost forgot about alternate data streams.

The basic idea is to install the driver or service so it can “Hide” itself and the other files registry keys and processes it needs to operate.

How it does this is by intercepting calls to list files or registry setting or process info or even disk info and can therefore remove from the results a predetermined set of files or registry or process entries  effectively hiding there existence so no one knows they are there by using normal means.

How do you find them?

They can be very difficult to find, again depending on how sophisticated they are.

First off you need to understand like in network analysis what is “Normal”  for a system.

The first step would be to determine if there is a process(s) running that do not conform to “Normal”, like multiple system process that should only have 1 copy of itself running, then you could check to see if there are items set to auto start on reboot that do not conform to “Normal” or any extra task that may have been added or network traffic to places it shouldn’t be going, also there are tools to check for encrypted files on the system.

Given what we know of the capability’s of these bad boys we can not be certain a system is “Clean” just because the system looks “Normal”. It could have these “Cloaking” drivers/ services running in the background.

The next step would start to delve into the world of computer forensics, where you would take a system off line and scan it for the traits previously mentioned, registry keys that shouldn’t be there, files that used to be hidden are now there or encrypted files where none should be, partitions that were not previously showing up thru the normal methods.

All of these and more could be a sign of some form of infection.(Or DRM Software)

I have been looking into service security and other information involved with them for over a year.

After reading several reverse engineering papers on different Rootkits and other malware one thing became clear and I developed a theory(educated guess).

The theory is a simple one . The implementation of it is not.

To the best of my knowledge every service or driver “Has” to “Register” with the system before it can do was it was intended to do. Whether or not it drops a file to disk or loads its code into memory and never touches the disk.

This is where I believe the weak point is.

The “Theory” is to have a service or driver “Listen” for services and  drivers being “Registered” with the system and, either scan it or at the very least log it and possibly send a administrator alert before it has a chance to infect a system and hide itself. The location of the log file would need to be well protected to keep anyone from removing it or changing its contents and thus continue to hide itself. Although the missing file itself would be a red flag that that system would need to be investigated further.If the file was changed or edited then it might prove difficult to verify what got changed in it and  thus back to the possible need to send an alert out also.

This way there will be a trail of what and when they got “Registered” with the system and possibly make the detection and removal faster. If it was somehow incorporated in a antivirus solution it may be even better as they already have a driver on the system.

The down side is if the system is already infected anything before that point would not be logged and any future ones may get filtered out by the Virus/Rootkit. You could also make a list of drivers not to be logged in order to keep down the log size but that would need to be protect also.

So far this is still a “Theory”, I still need to create a proof of concept program and build my malware lab to test against this “Theory” looking for any holes in it.

There are at least a few ways I can think of doing this and they start from the hard way to the very difficult as there seems to be no built in way to do this.

Lets see a little demonstration on how difficult it can be just to find a driver under normal conditions without being infected (that I know of ).

Have you ever wondered how Sysinternals Process Explorer works ?

In its most basic sense it just installs a device driver and listens for system calls and events and can report them back to the user application, Process Explorer, such as  process start or process end and information about each individual process and it’s threads and can give you a stack trace also for that moment in time and much more.

The question is how do you find this process or driver that it is using?

The driver does not show up in the MMC Services or in Device manager.

When using Process Monitor to view the launch of Process Explorer the driver name does not show up if it has already been launched once, it is still loaded into memory. Upon reboot run Process Monitor then run Process Explorer and then you can see the driver dropped to disk, loaded to memory then delete the file on disk.

On a 64 bit system Process Explorer starts the 32 bit version then extracts the 64 bit version to a temp folder then starts it and then extracts the driver to the system drivers folder to be loaded to memory before being deleted from disk.

So again how do we figure out what driver it is using. The easiest way it to also use the lower pane option of Process Explorer. If on a 64 bit system select the process “procexp64”  and select the view handles option on the toolbar.

Next sort by type then scroll down to the type “File” then look for an entry that starts with “\Device\drivername”.

In this case it is easy to spot. We are looking for “Device\PROCEXP 152” used with version 15.42 at least of Process Explorer.

Ok so how else can we find out what this driver name is.

If it is a normal driver install you can look into 2 places for the physical file.

“C:\Windows\System32\drivers” or if it is a 64 bit system “C:\Windows\SysWOW64\drivers” or one of the sub directory located in those folders but it could be dropped anywhere on the system, in the program files folder, in a temp folder, anywhere it may have read/write or read/execute access.

During my investigations I have created several tools to help with getting information more easily out of the registry and a system.

When a service or driver is registered it gets registered in the system registry under the keys:
“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services” and

The same name could possibly be loaded into both keys depending on the type of driver it is.

Lets start with what you see as the system normally shows you. The places that give information on installed services  are thru the MMC utility “Services” Which on my system shows 173 services(counting by hand). Using a program that utilizes the Service Process class also return 173 services, using WMI also returns the same number.

Then for the drivers you have Device Manager you also need to select the show hidden devices to show the Non Plug and play devices or “Legacy Devices’.

Viewing it this way in Device Manager is difficult to get a count on how many are there but what I want to relay is that using WMI Win32_SystemDriver only returns a list of 248 found on my system, but using the service process class again and selecting the type of driver instead of service it returns a list of 271 a difference of 23. I have created a new program to find the difference in what is missing. I can only assume that they are not listed thru WMI  because there is no physical file on disk for them (so far that I have found). Perhaps they are the ones that were uninstalled. I still have to track down each one and see what they do then build a database to log the information in.

Also the Process Explorer driver did not show up in either my regular tools or Device Manager to  reveal the name of the driver

Using this tool though:


Here we see that there were in fact three different versions of this driver registered with the system. All this tool does is enumerate the registry key. I have made it to be able to enumerate any control set numbers that it finds to check for any possible differences.

While writing this I had CurrentControlSet, Controlset001 and Controlset002. When I enumerated them the count was “1” different between CurrentControlSet, Controlset001 and Controlset002(diff). Viewing the list of both side by side I could not see the  difference so I saved a copy of the list for each and then rebooted into another OS partition loaded the registry key for this partition and then dumped it just in case something was hidden. Using my new List Diff tool with the first 2 list I saved before the reboot I was able to track the difference to a driver that I recently uninstalled .

Upon rebooting into this OS I then discovered there was no longer a ControlSet002 listed, instead there was a ControlSet003 and no ControlSet002 most likely because I did the uninstall and the list changed.

After a small test searching the system for files by name, the names that appears here in the list seems to be the actual name of the driver without the “LEGACY_” tacked on to the beginning.

Another tool I found while researching this, called DevManView from NirSoft, can get the same information from the registry once you enable the show non plug and play devices under the options tab.

I also tested if a Administrator could delete one of the entries. The current Security settings does not allow a “User” to change the security, take ownership or delete the key.

So in conclusion the best I can tell once a service or driver is installed on a system then it will stay on this list unless advanced methods are used to remove it or it gets uninstalled/unregistered and then system is rebooted. As long as the results of the enumeration are not filtered it should give a good history of what was loaded on the system and not uninstalled.

I am adding a link to my SkyDrive where I will have a zip files containing several tools the file name is BlogTools.zip. It will also have a readme file with the files names,description, and a how to use, most are pretty self explanatory on how to use.

Once this post gets consumed by the “RootAdmin” site I will try and add them there also.

I look forward to any comments or feedback.

Posted in RootAdmin | Tagged

How Do You Get A Service SID From A Service Name

Recently I have been trying to better understand Windows Service Security.

Starting with Windows Vista, Windows Services were able to have a SID Associated with the service so you could have Service Isolation and reduce the required privileges needed and to still have the ability to access the required registry and files it needed to, and also have the ability of restricting other SID’s to it’s resources.

So I was wondering, how do you get a SID for a service you create ?

The only built in tool I could find was the SC.Exe utility console application and use the command “sc.exe showsid ServiceName”

I then got wondering how does that program get the SID for the service ? Has it already been created and its just getting it from some location ?

After some internet searching I ran across a Wikipedia page for  Security Identifier and it said the the SID is created using the service name in uppercase and running it thru the SHA-1 Hash function.

So I created a small application that did that but it was not producing the same output as the SC.Exe utility. So I was still missing something.

I then started cmd.exe with a debugger, then ran the command “sc.exe showsid ServiceName” , that gave me a little bit of information but not enough to solve my problem of outputting the wrong results.

Next I turned to a Utility named EXE Explorer to view what the imports were for the Sc.exe utility and found that it imported a function from ntdll.dll named  RtlCreateServiceSid. That function Is undocumented.

Next I copied the ntdll.dll to another folder and opened it with a program named PEBrowser64 Professional. Then found and  decompiled that function and seen it was in fact using the Sha1 has function.

Next I Looked at the RFC 3174 SHA1 specification to see if they were doing anything different.(They were not,best I could tell)

Going back to PEBrowser64 Professional and after tracing that function thru several other functions you can get the basic steps required to output a Service SID from the input service name.

Basic Steps

1: Input service same.

2: Convert service name to upper case.

3: Get the Unicode bytes()  from the upper case service name.

4: Run bytes() thru the sha1 hash function.

5: Reverse the byte() string  returned from the SHA1 hash function (on Little Endian systems Not tested on Big Endian systems)

6: Split the reversed string into 5 blocks of 4 bytes each.

7: Convert each block of hex bytes() to Decimal

8: Reverse the Position of the blocks

9: Create the first part of the SID “S-1-5-80-“

10: Tack on each block of Decimal strings with a “-“ in between each block that was converted and reversed.

11: Finally out put the complete SID for the service.

This is not the exact order that the API call used but the order I used in my test application.

Here is what the final test application looks like.


This test application currently only takes input from the combo box that is filled with the service names it found.

The next problem was to figure out why I was getting a Different result so I broke down an Existing SID to Hex to be able to compare and to help figure this out.



391397178   = 0x17543F3A ,Reverse Order = 0x3A3F5417
1713532359 = 0x66226DC7 ,Reverse Order = 0xC76D2266
3388783719 = 0xC9FCBC67 ,Reverse Order = 0x67BCFCC9
1671243502 = 0x639D26EE ,Reverse Order = 0xEE269D63
2983178441 = 0xB1CFB0C9 ,Reverse Order = 0xC9B0CFB1

My initial output looked like.



Wrong results, But it agreed with all SHA1 Utility’s I tried.

After some more internet searching I ran across a question on Stack Overflow named Unicode, UTF, ASCII, ANSI format differences down at the bottom there was a link to another article named Joel on Software: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

After reading that I then searched on Code project for “RFC 3174” and ended up with this article http://www.codeproject.com/Articles/2463/CSHA1-A-C-Class-Implementation-of-the-SHA-1-Hash-A

I Read the article, downloaded the sample project and built it, and then ran it inputting the uppercase service name and it returned,

Enter the string to hash:

Hash of the ANSI representation of the string:

Hash of the Unicode representation of the string:

Now looking at that confirmed the the RtlCreateServiceSid function was not doing anything strange with the SHA1 function.

The top one matched what I was outputting in the original version but the bottom was lining up with the SID I had broken down.

So what was the problem of the different results ?

I then went back to my code and took a look and made a change on the Input Decoder from.

Dim encoder As New System.Text.ASCIIEncoding


Dim encoder As New System.Text.UnicodeEncoding

After that I was getting the results of .


Now I knew I was onto something and the rest was a matter of following the steps mentioned above.

Lets take a closer look to understand why I was getting the wrong results.

A while back I created a utility that input a string and output as hex string encoded with various Unicode encoders.


As you can see from the screenshot above that using the ASCII encoder output as :
4D 53 4D 50 53 56 43   And the Unicode output as 4D 0 53 0 4D 0 50 0 53 0 56 0 43 0

The first is 7 bytes long but the second one is padded with zeros and is 14 bytes long.
Looking thru the RFC 3174 you find that the whole thing starts with the length of the “message” in binary bits and using a different format will drastically change the outcome of the hash function due to the different lengths..

Another thing you may notice in this application is the BigEndianUnicode.


If you will take a close look of that, the bytes are going the same direction but the lead and trailing Zeros are on opposite ends.That would lead me to believe that the “Plain” Unicode is considered Little Endian byte order. Correct me if I am wrong.

The last part of the puzzle is after you create a SID how do you associate it with the service name ?

The answer can be found on Service Changes for Windows Vista under the sub section “Service Isolation”.

The last part reads : “To set the service SID, call ChangeServiceConfig2 with SERVICE_CONFIG_SERVICE_SID_INFO.”

In order to have your service have a SID set automatically you must set a value for “SERVICE_SID_INFO structure” to either “SERVICE_SID_TYPE_RESTRICTED” or “SERVICE_SID_TYPE_UNRESTRICTED” if you set it as “SERVICE_SID_TYPE_NONE” then it will not automatically be set and available for use by your service to aid in securing it.

Just being able to create a SID using this tool does not enable you to be able to use that SID without setting the required parameters above, It would only show you the same SID that the “sc.exe showsid ServiceName” does.

For instance If I was to run a service name like “mySuperService” thru the SC.exe utility it returns.

NAME: mySuperService
SERVICE SID: S-1-5-80-1956725871-603941828-2318551034-3950094706-3826225633

But that does not mean the service is installed on the system or the SID could be used.

It is just the SID created from the name.

Although it may help to know what it will be after being set or while building a Security Descriptor and setting the rights.

I have not found a command using the SC.Exe utility to set the service SID yet.

It may need to be done thru your service installer or a controller application.

The “Sc config servicename” does not seem to have the setting in it for setting the Service SID Type. I’m still looking into it.

That’s it for now I’m still working on a more overall service security article mainly aimed towards understanding why my program stopped working for shutting down Microsoft Security Essentials. Windows 8.1 has something new for Anti-Malware programs.

Posted in RootAdmin | Tagged

Get the last write time of a registry key in VB.Net using PInvoke

For a while I have been trying to find out how to get the last write time of a registry key but after digging thru the object browser in VS 2008 and .Net Framework 3.5 there appears to be no built in function to get the last write time for the registry.

After allot of internet searching using various search terms I finally decided to try to use Platform Invoke (PInvoke). I understand what it does but have never been able to get anything to work that wasn’t just a matter of copy paste and maybe some tweaking of the code to get it to do what I wanted.

The hardest part for me is the data types in unmanaged C/ C++ and in VB.Net and the Marshaling of them between managed and unmanaged. It can get very confusing on what they are wanting or how to do it.

A big help to get this project off the ground quickly was a Win API wrapper class on Code Project called VB.NET wrappers for much of the Windows API by andrewbb@gmail.com

I was able to just copy/paste the parts that I needed to a Class and then try to figure out the one function I needed that was not in the download.

The C++ Function I found to get the last write time is “ RegQueryInfoKey ” on MSDN.

LONG WINAPI RegQueryInfoKey( _In_ HKEY hKey, _Out_opt_ LPTSTR lpClass, _Inout_opt_ LPDWORD lpcClass, _Reserved_ LPDWORD lpReserved, _Out_opt_ LPDWORD lpcSubKeys, _Out_opt_ LPDWORD lpcMaxSubKeyLen, _Out_opt_ LPDWORD lpcMaxClassLen, _Out_opt_ LPDWORD lpcValues, _Out_opt_ LPDWORD lpcMaxValueNameLen, _Out_opt_ LPDWORD lpcMaxValueLen, _Out_opt_ LPDWORD lpcbSecurityDescriptor, _Out_opt_ PFILETIME lpftLastWriteTime );

Reading thru the information of the MSDN page, you can not just use that function by itself you must first call one of four other another functions passing in the Base Key as a integer value and the sub key as a string, if it is not just a base key, and then return a handle to the key.
Since all I wanted to do was read an existing key and not set the value or write to a new one I used  “RegOpenKeyEx” on MSDN.

LONG WINAPI RegOpenKeyEx( _In_ HKEY hKey, _In_opt_ LPCTSTR lpSubKey, _Reserved_ DWORD ulOptions, _In_ REGSAM samDesired, _Out_ PHKEY phkResult );

The input parameters of “RegOpenKeyEx” are the Integer value of the Base Registry key (below) as found in WINDDK in the file WINREG.h, Optional sub key as a string and the integer value of the “Registry Key Security and Access Rights” of   KEY_QUERY_VALUE (0x0001) Required to query the values of a registry key (see “RegQueryInfoKey”On MSDN ).

#define HKEY_CLASSES_ROOT (( HKEY ) (ULONG_PTR)((LONG)0x80000000) ) #define HKEY_CURRENT_USER (( HKEY ) (ULONG_PTR)((LONG)0x80000001) ) #define HKEY_LOCAL_MACHINE (( HKEY ) (ULONG_PTR)((LONG)0x80000002) ) #define HKEY_USERS (( HKEY ) (ULONG_PTR)((LONG)0x80000003) ) #define HKEY_PERFORMANCE_DATA (( HKEY ) (ULONG_PTR)((LONG)0x80000004) ) #define HKEY_PERFORMANCE_TEXT (( HKEY ) (ULONG_PTR)((LONG)0x80000050) ) #define HKEY_PERFORMANCE_NLSTEXT (( HKEY ) (ULONG_PTR)((LONG)0x80000060) ) #if(WINVER >= 0x0400) #define HKEY_CURRENT_CONFIG (( HKEY ) (ULONG_PTR)((LONG)0x80000005) ) #define HKEY_DYN_DATA (( HKEY ) (ULONG_PTR)((LONG)0x80000006) ) #define HKEY_CURRENT_USER_LOCAL_SETTINGS (( HKEY ) (ULONG_PTR)((LONG)0x80000007) )

You can add the list above as a public Enum in your API class I’m just using the 4 basic ones as supplied with the API wrapper class.

In VB.Net it might look like.

Public Enum Hives As Integer HKEY_CLASSES_ROOT = &H80000000 HKEY_CURRENT_USER = &H80000001 HKEY_LOCAL_MACHINE = &H80000002 HKEY_USERS = &H80000003 HKEY_PERFORMANCE_DATA = &H80000004 HKEY_PERFORMANCE_TEXT = &H80000050 HKEY_PERFORMANCE_NLSTEXT = &H80000060 '#if(WINVER >= 0x0400) HKEY_CURRENT_CONFIG = &H80000005 HKEY_DYN_DATA = &H80000006 HKEY_CURRENT_USER_LOCAL_SETTINGS = &H80000007 End Enum

Next use the handle returned from “RegOpenKeyEx” as input to “RegQueryInfoKey” and return just the parts of the function you want. Since all I want here is “Last write time” then all I needed was the pointer for input and a output variable of type “FILETIME”.

When looking at the the the unmanaged data type of “Long” , “Platform Invoke Data Types” we see that that data type in managed (CLR) is  System.Int32 and on Data Type Summary (Visual Basic) we see that the System.Int32 is Integer in VB.Net.
So the “Public Enum Hives As Integer” should be the correct data type.

Another thing I noticed, was when I tested changing from Integer to Uint32 which is what I thought it might be till I looked it up, I ended up with some strange errors, one was a math overflow and another was something about a number being to big or to small for the data type. After changing back to type Integer the problems were gone.
I was testing using a long set of sub keys and then reducing by 1 level till I got to the base key, it was there that the errors showed up until I changed the data type back to Integer.

Here is what the current test project looks like.


This version inputs a registry path to a key and then outputs what you see above.

I split the base key from the sub key section, Output what the value of the pointer was for the key returned by “RegOpenKeyEx”, returned the HResult for both API calls, if it returns anything but 0(Zero) for the Result then there is an error.
Next I have the returned File time listed as the high and low parts. Finally I output the converted “FILETIME” structure to date time using a API converter found in the  API Wrapper class.

I was wanting to do some kind of validation for the input so I created two crazy functions to parse the input.

The first thing I always test for is if the input text box has anything in it or not.

Next is when it gets strange, I was first testing with just longs paths that had several “\” in them so I was using that for my split character but when I tried to test just a base key I forgotten about that and had to add more handling of the input.

'Test first if we are dealing with a base key or malformed entry If Not input.Contains("\") Then If isabasekey(input) = True Then ' just do the work on the base key BaseKey = input ElseIf isabasekey(input) = False Then 'this is a messed up input tbOutput.Text = "Input not in Correct format" Exit Sub Else End If 'if the input has a base key and subkeys then parse the string to get the base key and the subkey values ElseIf input.Contains("\") Then BaseKey = input.Substring(0, input.IndexOf("\")) 'Trims everything to the right of and including the first "\" trimlength = input.IndexOf("\") 'The string index location of "\" SubKey = input.Substring(trimlength + 1) 'Trims everything to the Left of and including "\" End If

As you can see I have lots of comments in the code.
The First part takes the input string and if it contains a “\” then just proceeds on to the split function in the ElseIf statement , if it doesn't contain a “\” then is it a Base key or just a malformed entry. So it gets put thru the “isabasekey” function (great naming, I know)

Private Function isabasekey(ByVal akey As String) As Boolean Dim key As String = akey.ToUpper If _ key = "HKLM" OrElse _ key = "HKEY_LOCAL_MACHINE" OrElse _ key = "HKCU" OrElse _ key = "HKEY_CURRENT_USER" OrElse _ key = "HKCR" OrElse _ key = "HKEY_CLASSES_ROOT" OrElse _ key = "HKU" OrElse _ key = "HKEY_USERS" Then Return True Else Return False End If End Function

Here I simply take the input value and change it to upper case then compare the input to the allowed values and if it matches one it returns true if not then false.
Next after returning from the function if it is a valid allowed base key then just set the variable, BaseKey = input value. if it returns false then set the output text box text to the error message and then exit the sub, no reason to continue.

Next I take the “BaseKey” value and run it thru a function to validate it and return the integer value of the base key to use in the first API call.

Private Function ParseInput(ByVal key As String) As Integer Dim op As String If key.StartsWith("HKLM") OrElse key.StartsWith("HKEY_LOCAL_MACHINE") Then op = Hives.HKEY_LOCAL_MACHINE ElseIf key.StartsWith("HKCU") OrElse key.StartsWith("HKEY_CURRENT_USER") Then op = Hives.HKEY_CURRENT_USER ElseIf key.StartsWith("HKCR") OrElse key.StartsWith("HKEY_CLASSES_ROOT") Then op = Hives.HKEY_CLASSES_ROOT ElseIf key.StartsWith("HKU") OrElse key.StartsWith("HKEY_USERS") Then op = Hives.HKEY_USERS Else op = -1 '"Input Value Not Supported or Not in correct format" End If Return op End Function

Next we can finally make our first API call passing in the Integer value we just got for the BaseKey, the Sub Key string part that we split above if we are checking that along with the base key,then a variable to hold the return value for the pointer to the key.

Dim regkeyptr As Integer 'Here is our first API call to RegOpenKeyEx inputing the base key value and the subkeys value if any. 'openregkeyResult is th Hresult of the call. regkeyptr is the returned intptr for the input key. 'The fourth parameter is set at a default Constant value. Dim openregkeyResult As Integer = RegOpenKeyEx(BaseKeyValue, SubKey, 0, KEY_QUERY_VALUE, regkeyptr)

The third parameter is Reserved and the MSDN site says it must be set to 0(zero) the call also worked with it set as Nothing, but I changed it once rereading the description.

Next we can make our call to “RegQueryInfoKey” imputing the the pointer we got back and and a variable to hold the return value for the FILETIME structure.

'create a filetime structure to recieve the returned time Dim lpftLastWriteTime As System.Runtime.InteropServices.ComTypes.FILETIME Dim returnvalue As Integer returnvalue = RegQueryInfoKey( _ regkeyptr, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ Nothing, _ lpftLastWriteTime)

As you can see above all the parameters we don’t need are set to nothing. Just the input of the pointer and output of the FILETIME have values. The “returnvalue” is the HResult and should be 0(zero) or else something went wrong.

Next we use a convert function supplied by the API class to convert this to a date time, then output and we are done.

While doing more testing I realized that I had forgotten to add one other input validation at the start, you always test in some way the users input path to see if it exist.  

See if you can see what is wrong here.


Now look close, what is wrong with the output here ? Compare the two if you need to.

Ok times up.

The first thing you may notice is the last letter is missing from the path.
The next thing you may notice is that the returned pointer from the first API call is 0(zero) ,this return should be non 0 ,bad sign.
Next the result from the open key API is 2 in decimal  System Error Codes (0-499) which translates to “ERROR_FILE_NOT_FOUND 2 (0x2) The system cannot find the file specified” which is kind of strange for this one. Next we see the result from the second API call as 6 in decimal. “ERROR_INVALID_HANDLE 6 (0x6) The handle is invalid.” now that one makes more sense.
The last thing you may notice is the output date/time.
The FILETIME structure page tells us.
”Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).” But looking at that is shows the low and high times as 0(zero) and the output is off 6 hours from what it should be. To answer that I just happen to be in the time zone –6 UTC but my current time offset is –5 for daylight saving time

Now that API class had two conversion functions, the one above should be for Local time without adjusting for Daylight Saving Time. The other returns the UTC time.


Now after changing which date/time converter was used all of the output is the same except the date/time output which now aligns with the date/time it is supposed to be for UTC time if the FILETIME value is not added to it.
Also if you input a path that contains a “Value Name” at the end it returns the same as if it was a bad path. So it only works with valid key paths.

One more thing.  After we get done using the ‘RegOpenKeyEx” function we need to close the key so we use the “RegCloseKey” function supplied by the API wrapper class.
Just pass in the handle returned from
‘RegOpenKeyEx” and that’s it.

So after a little more thought, time, and effort I should be able to turn this into a wrapper class to be able to drop into any project that requires the the ability to return the last write time of a registry key. Just input a valid path and get back a Date/Time and hide the rest of the nuts and bolts like most .Net classes do.

I hope someone found this useful.


RegOpenKeyEx function
RegQueryInfoKey function
FILETIME structure
System Error Codes
Platform Invoke Data Types
Registry Key Security and Access Rights
Data Type Summary (Visual Basic)
VB.NET wrappers for much of the Windows
RegCloseKey function

Posted in CodeProject | Tagged , ,

Add a new line instead of a new paragraph in Live Writer

I have been looking for some time on the best way to just start a new line instead of always adding a extra line when you hit the enter key.

Here is a sample.

Here after hitting the enter key.

New Sample.
After hitting Shift+Enter instead of just Enter.

You do not find this simple method listed in any of the keyboard Shortcut list.

Now I can format my post more the way I want.
Some times I have to hit the enter and back space a couple of times before it works.
Could be a wore out keyboard though.

I am posting this so I can find it again myself.

Posted in Uncategorized | Tagged

What’s in your toolbox (Environment Variables)

Over the years I have found several tools that make a good or great addition to my PC Repair toolbox. Some you use every day and others you only pull them out on occasion.

I hope to write up several that I have used.

This first one came when I was looking recently for a better way to check and modify the Environment Variables for the system.

The normal way is to go to Control Panel –> System –> Advanced System Settings –> Environment Variables . Then you could scroll and find the variable name you want in the list, and then select the name, and then click edit, then scroll the long string or copy paste to notepad then make the modifications, then copy paste back and save. As you can see below it is not easy to see what you have there especially if there are allot in one line.


Now that is not very handy for viewing the big picture. You can not even make the window bigger. Even if you click the edit button for a variable name it is hard to work with and you may still need to copy to notepad, edit it then copy back.

The most promising program I found on a internet search was one called “RapidEE” located here http://www.rapidee.com/en/download .

What I like the most is it comes as an Installer or as a Zip file so you can drop it on a flash drive and  use it portably without having to install it if you have several systems to look at.

This tool also has a built in crash report system. So if it crashes for some reason you can save the report locally or allow it to be sent to the creator if the need would arise.

When I first tested this program (Version 7.1 Build 841) it had a bug in it that crashed the application in a seemingly random way. After several emails back and forth with the Creator I was able to send the instructions to make the bug reproducible and gave my comments on what was displayed in the bug report and within a day they were able to find and correct the bug now that they could consistently reproduce the bug.

So if you tried that version (7.1 Build 841) and blew it off, try the latest version, as it works well.

So far I haven’t been able to break it again : )

The program starts up as standard user in Vista (the OS this was tested in) and you are able to edit the user variables but not the the system variables until you restart the program as an Administrator or right click and run as admin from the beginning.

It has a button to restart as admin. too.


As you can see by the screenshot above there are some variable names in red, that indicates that there is a bad path in the variable name.  If a program removes its environment variable path from the list it can still show up as red because a “Null” or Empty path will show as an  error. You see that when you have an entry like “path,,path”. The two commas side by side like that indicate that a environment variable path was removed from between the two paths.

You may also notice the warning icon at the bottom. That indicates that you are running the program as a standard user and would need to elevate in order to edit the system variables.

Before making any changes or if you are just curious where the settings are stored you may want to (run as admin) go to the file menu and select backup. This will back up all of the setting as a registry key backup for both the user and system environment variables.

By backing them up before hand you can restore them if you make a mistake or something goes wrong while editing them. Like me. I hate when the mouse jumps and you delete the wrong thing with out first backing it up. (That’s my story and I’m sticking to it.)

This tool will change them instantly after you press the save but they will not be reflected till the next boot if the variable path is currently in use, that I have found so far.

Lets talk about a few of the other features.

You may be able to see above that the variable names are displayed as a tree view. You can expand the individual variable name and edit or view the the individual paths. You can also narrow in on the item that caused the variable name to be flagged red.

As you install and uninstall programs sometimes the variable paths are left after uninstalling them leaving several bad paths. I should have taken a screen shot before I did the edit so you can see what variable path were left behind on my system from programs that were tested, there were several.

The two windows are for the user and the system variables that you can edit. The user variables can be edited with out being elevated. There are  several right click menu items for both sides.

When you first start out in standard user mode you will notice that there are some variable names that are greyed out and you can’t edit them, For the most part you would never need to edit those as the system takes care of those and might cause a few problems if you just deleted them. (good feature for those that don’t know what the variables do)

The right click options allow you to add new variable names, add new paths, reorder the path variables in the list, and more.

This can be a great tool for quickly going in and get a quick overview on what is there.

Checking for bad or missing paths, or for quickly adding a new path to a variable name.

If you haven’t already checked this one out then give it a spin.

This program is “Free Ware” / “Donation Ware” so if you like it, then you how that works. (there is a button for that too)

I hope others find this tool as useful as I did.

That’s it for this time.

Posted in RootAdmin

Get the Security Descriptor of a Windows Service With WMI

Reason for Project:

A while back I wrote a little utility to temporarily turn off the Microsoft Security Essentials Anti-Virus / Anti-Malware service.

In January of 2013 I discovered that my application no longer worked due to an Access denied error. I started to investigate why and discovered my application no longer had the “Rights” to shut down the service even though it was “Run As Admin”.

In order to understand what rights I or an application I wrote would have over a service I needed to see what Users and  Rights were assigned to the service. That would also explain why I no longer could control the service thru the built in Service Controller.

Back story:

Beginning with Windows Vista we now have Service isolation, Service Hardening and the ability to assign a SID to the service to help secure it more. You also need to secure the registry where the information about your service is listed so it can not be changed by unauthorized users or code.

The service can be set up with “Default permissions” or set with “Special permissions”, effectively locking it down to help keep users or malicious code from messing with it.

This is just one of many articles on the subject. Services Hardening in Windows Vista here.

So services just keep getting better protection to help keep malicious code from stopping or compromising a service.

The Code:

For several months I have been looking into finding a way to get the security descriptor from a service but no sample code could be found at the time. I asked the question on Code Project but did not get an answer.

The .Net framework  has a built in way to get at the security descriptor for the Registry,Folder, and Files but after going thru the Object browser (.Net 3.5) for some time I could not find a way to do it without creating the needed wrapper classes like was done for the other items.

So I decided to try to Platform Invoke with  the “QueryServiceObjectSecurity” function here.

After several attempts and asking another question on Code Project and not getting an answer I kept digging  and am now close but not quite there yet on getting it to work.

Now that I have a working model I can see what I am missing and possibly get it to work.

In the mean time while still researching how to do it I stumbled across a question that was asked on Stack Overflow here titled “Getting Win32_Service security descriptor using VBScript”. That question gave me a starting point to try VB script. Further research landed me on the MSDN page where I think it was originally taken from. here. WMI Security Descriptor Objects.

After seeing that, I was able to work out the following VB Script to get the Security Descriptor for all services.

VB Script:

'Tested Script Still works with the values commented out. 'SE_OWNER_DEFAULTED = &h1 'SE_GROUP_DEFAULTED = &h2 'SE_DACL_PRESENT = &h4 'SE_DACL_DEFAULTED = &h8 'ACCESS_ALLOWED_ACE_TYPE = &h0 'ACCESS_DENIED_ACE_TYPE = &h1 strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate, (Security)}!\\" & strComputer & "\root\cimv2") ' Get an instance of Win32_SecurityDescriptorHelper Set objHelper = GetObject( _ "winmgmts:root\cimv2:Win32_SecurityDescriptorHelper" ) Set colServices = objWMIService.ExecQuery _ ("Select * from Win32_Service") For Each objService in colServices Wscript.Echo "Name: " & objService.Name ' Get security descriptor for Service Return = objService.GetSecurityDescriptor( objSD ) If ( return <> 0 ) Then WScript.Echo "Could not get security descriptor: " & Return wscript.Quit Return End If If ( return = 1 ) Then WScript.Echo "The request is not supported: " & Return wscript.Quit Return End If If ( return = 2 ) Then WScript.Echo "The user did not have the necessary access: " & Return wscript.Quit Return End If If ( return = 8 ) Then WScript.Echo "Interactive process: " & Return wscript.Quit Return End If If ( return = 9 ) Then WScript.Echo "The name specified was not valid: " & Return wscript.Quit Return End If If ( return = 21 ) Then WScript.Echo "The parameters passed to the service are not valid: " & Return wscript.Quit Return End If ' Convert Service security descriptor from ' Win32_SecurityDescriptor format to SDDL format Return = objHelper.Win32SDToSDDL( objSD,SDDLstring ) If ( Return <> 0 ) Then WScript.Echo "Could not convert to SDDL: " & Return WScript.Quit Return End If WScript.Echo SDDLstring WScript.Echo "" WScript.Echo "" Next

This script requires the “Security” Keyword and to be run as Admin for it to work.

That little script took several hours to work out once I got a clue on what needed to go into it.

For the most part it is self explanatory.

The part I had trouble understanding was the Object, “objSD”.

As it turn out that is the Security Descriptor itself. That was the out Object from the call to “GetSecurityDescriptor” it was also an In Object to “Win32SDToSDDL”. I did not totally understand this until I completed the VB.Net version of this coming up next.

The script version can be modified to just check one service name or, used “as is” , to get all of them.


The VB.Net version was a little tougher. I first tried to work it out by starting out with the code generated from my GUI WMI Code Creator but that wasn’t working.

I could probably count on one hand the number of times in the last fifteen years that I needed to “Invoke” a method in a WMI class in order to get the information I needed. So I have to relearn how every time.

Here is the Final Project:


And here is what it looks like after formatting by hand in the textbox.


As you can see you get the Owner, Group, DACL, and the SACL.

Lets start with where the program starts Filling in the combo box.

I wanted to test two different ways to see how they worked.

The first is getting the list by using the “System.ServiceProcess” class.

Remember you have to add that “reference” before you can “Imports” it or use it in your code.

Private Sub FillServiceNames() 'Use ServiceProcess class to fill in the names Try Dim svcs() As ServiceController = ServiceController.GetServices() Dim svcCtlr As ServiceController For Each svcCtlr In svcs cbServiceNames.Items.Add(svcCtlr.ServiceName) count = count + 1 Next lblCount.Text = "Services Found: " & count.ToString Catch ex As Exception MsgBox(ex.Message) End Try End Sub

It is pretty straight forward.

Next is using WMI to fill the Combo Box.

Private Sub FillComboBox() ' Use WMI to fill in the Names Try Dim searcher As New ManagementObjectSearcher( _ "Root\CIMV2", _ "SELECT * FROM Win32_Service") For Each queryObj As ManagementObject In searcher.Get() cbServiceNames.Items.Add(queryObj("Name")) count = count + 1 Next lblCount.Text = "Services Found: " & count.ToString Catch ex As Exception MsgBox(ex.Message) End Try End Sub

They both make a call to get a list of services and then do a for each loop and then fill the Combo Box with the service name.

That was the easy part.

When I first couldn’t figure out the proper way to invoke a method using trial and error, I tried to use the “Strongly Typed” classes generated by “MgmtClassGen.exe” to get a better idea of what they were wanting, but I was still getting errors and could not figure out some of the required parameters. After several hours of that I tried the (Original) WMI Code creator. It has a section that generates code that works with methods. The code produced by that was not working and there was one parameter that I just could not figure out what it was supposed to be.

My next Idea was to turn to a program called “WMI Delphi Code Creator” found here.

I was given a link to it after I posted my article on my GUI WMI Code Creator, which does not do methods.

That program was able to produce code using methods like the (Original) WMI Code Creator but it was in C#, so I had to convert it to VB.Net.

So those that want a C# version may want to check it out.

(Side Note on that program, if you don’t want it calling home for updates then uncheck that option in the options section. Also they have a install and standalone version)

After generating the two required methods to get the security descriptor and to transform it from a Win32SD to a SDDL string form I dropped the code into the project and started piecing it together.

After several hours of working with the code and reading more on it and fixing several items that used the same names in both generated methods, the code finally went all of the way thru and output to the text box.

I was so happy that it stopped crashing that at first I didn’t understand the results that were returned.

There was no SDDL String returned and I had two different error codes retuned that was not listed in the normal WMI return Codes. The first one returned from the “GetSecurityDescriptor” method was this, 2147943714 (Converted)0x80070522.

As it turns out it is a privilege not held error from the system not WMI.

So back to the “Strongly Typed” classes to see if I could figure out what I was missing. I found this in the Function “GetSecurityDescriptor”

Dim EnablePrivileges As Boolean = PrivateLateBoundObject.Scope.Options.EnablePrivileges PrivateLateBoundObject.Scope.Options.EnablePrivileges = true

That gave me the answer.

So without further ado, here is the final code.

Try Dim strbldr As New StringBuilder Dim svcName As String If cbServiceNames.SelectedIndex = Nothing Then MsgBox("No Service Name Was Selected") Exit Sub Else svcName = cbServiceNames.SelectedItem.ToString End If Dim ComputerName As String = "localhost" Dim Scope As ManagementScope If Not ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase) Then Dim Conn As New ConnectionOptions() Conn.Username = "" Conn.Password = "" Conn.Authority = "ntlmdomain:DOMAIN" Scope = New ManagementScope([String].Format("\\{0}\root\CIMV2", ComputerName), Conn) Else Scope = New ManagementScope([String].Format("\\{0}\root\CIMV2", ComputerName), Nothing) Scope.Options.EnablePrivileges = True 'Error returned without EnablePrivleges 2147943714 (Converted)0x80070522 End If Scope.Connect() Dim Options As New ObjectGetOptions() 'Dim Path As New ManagementPath("Win32_Service.Name=""AdobeARMservice""") Dim Path As New ManagementPath("Win32_Service.Name=" & "'" & svcName & "'") Dim ClassInstance As New ManagementObject(Scope, Path, Options) Dim inParams As ManagementBaseObject = ClassInstance.GetMethodParameters("GetSecurityDescriptor") Dim outParams As ManagementBaseObject = ClassInstance.InvokeMethod("GetSecurityDescriptor", inParams, Nothing) Dim operrHex As String = String.Format("0x{0:X2}", outParams("ReturnValue")) Select Case outParams("ReturnValue") Case 0 ' No Problem continue on. Case 2 MsgBox("Error Code 2" & vbNewLine & "The user does not have access to the requested information.") Exit Sub Case 8 MsgBox("Error Code 8" & vbNewLine & "Unknown failure.") Exit Sub Case 9 MsgBox("Error Code 9" & vbNewLine & "The user does not have adequate privileges.") Exit Sub Case 21 MsgBox("Error Code 21" & vbNewLine & "The specified parameter is invalid") Exit Sub Case Else MsgBox("Error Not Listed" & vbNewLine _ & "Error Code" & vbNewLine _ & outParams("ReturnValue").ToString _ & vbNewLine & "Hex:" _ & vbNewLine & operrHex) Exit Sub End Select '******************** 'Start the conversion '******************** Dim Path2 As New ManagementPath("Win32_SecurityDescriptorHelper") Dim ClassInstance2 As New ManagementClass(Path2) Dim inParams2 As ManagementBaseObject = ClassInstance2.GetMethodParameters("Win32SDToSDDL") inParams2("Descriptor") = outParams("Descriptor") Dim outParams2 As ManagementBaseObject = ClassInstance2.InvokeMethod("Win32SDToSDDL", inParams2, Nothing) strbldr.AppendLine("Security Descriptor in SDDL Format") strbldr.AppendLine() strbldr.AppendLine("Service Name:" & svcName) strbldr.AppendLine() Dim opstr As String If outParams2("SDDL") = Nothing Then opstr = "SDDL String is nothing" Else opstr = outParams2("SDDL") End If strbldr.AppendLine(opstr) tbOutput.Text = strbldr.ToString Catch ex As Exception MsgBox(ex.Message) End Try End Sub

The first part of the code just checks to see if a service name was selected from the combo box. If it was it sets “svcName” to the name that was selected.

Next starts the generated code. This code is setup for Local and Remote use, I left it as is.

This part checks to see if you are connecting to WMI on the local system or on the network  if yes then it inputs the network Credentials if not then it doesn’t use that parameter thus it is set to “Nothing” versus “Conn” as above.

As I hinted to above, The whole trick to get this to work with out that error above is to use the “Scope.Options.EnablePrivileges = True” setting. Then Scope.Connect() .

Next we set up to connect to the given service name and call the “GetSecurityDescriptor” method.

This is the one of the lines that gave me so much trouble.

Dim outParams As ManagementBaseObject = ClassInstance.InvokeMethod("GetSecurityDescriptor", inParams, Nothing)

In VB Script it is.

' Get security descriptor for Service Return = objService.GetSecurityDescriptor( objSD )

What is objSD ? it is not Declared anywhere like VB.Net. But it just works in the script.

And the WMI Code Creator Output.

' Execute the method and obtain the return values. Dim outParams As ManagementBaseObject = _ classInstance.InvokeMethod("GetSecurityDescriptor", Nothing, Nothing)

I couldn’t figure out what they were wanting for a “ManagementBaseObject” when I was working with the “Strongly Typed” classes.

The next line is just there so that if you receive an error other than the normal ones in the list it will get the hex version of the Error and add that to a message box.

Next we set up the Select Case Statement using the Values that would be output.

Next we set up for the Conversion.

Now we are at the line that I had so much trouble understanding what they were wanting.

inParams2("Descriptor") = outParams("Descriptor")

In VB Script:

Return = objHelper.Win32SDToSDDL( objSD,SDDLstring )

In the WMI Code Creator you had to give a name, who knew what it was supposed to be.

' Add the input parameters. inParams("Descriptor") = InsertNameHere

The WMI Code Creator listed it as type “Object” but that was not very helpful.

By the “objSD” in the script I assumed it was a Security Descriptor Object but didn’t know that was the Object you worked with. So it is the out parameter of the first call. In this case it was called amazingly,  “outParams”.

The last parts just sets up the string builder to get the name of the selected service.

Then verifies there is actually an output for the SDDL String.

Then finally write the information out to the textbox.

Once you understand what the input and output is expecting then it makes the job easier.

Later I may still go back and see if I can complete the Platform Invoke version just to see if I can figure it out now that I know what is involved using other methods.

I hope you learned something because I did.

SC.exe Utility

To get the DACL using this utility use the command

sc sdshow ServiceName

To get the SID of a service use the command

sc.exe showsid ServiceName

Note: this command uses Undocumented API calls to create a SID from the ServiceName.

Final Notes:

These functions should only work on Vista and above.



Getting Win32_Service security descriptor using VBScript


GetSecurityDescriptor method of the Win32_Service class


Win32SDToSDDL method of the Win32_SecurityDescriptorHelper class


WMI Security Descriptor Objects


Privilege Constants (WMI)


Executing Privileged Operations


Changing Access Security on Securable Objects


WMI Return Codes


WMI Error Constants


WMI Delphi Code Creator


Service Changes for Windows Vista


Service Accounts Step-by-Step Guide (AD)


WMI .NET Scenarios


Posted in CodeProject | Tagged , ,