Since Local User Group Management doesnt seem to be something I can query in C#, I figured I would do a down and dirty version of what I an trying to accomplish and I need some advice for best practices.
THE GOAL:
Essentially, I need to determine if the current local user's password is set to expire and display the result in a textbox. I have already written a text search so I dont mind that there will be extra data sitting on the output since I will distill the data down to a boolean check for one string.
The Problem:
Check out the code below. For some reason the error codes come through from CMD just fine, however, the output does not display in textbox2.
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\').Last(); ;
user.Text = userName;
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.Arguments = "/c net user";
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.RedirectStandardError = true;
cmd.Start();
//* Read the output (or the error)
string output = cmd.StandardOutput.ReadToEnd();
textBox2.Text = output;
string err = cmd.StandardError.ReadToEnd();
textBox2.Text = err;
cmd.WaitForExit();
First you're putting the output variable textBox2.Text, then you are replacing the textBox2.Text with err and I believe you're getting nothing from err variable, that's why TextBox2 is not displaying what you're expecting.
try to run the snippet below to check how output and err variable are getting:
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\').Last(); ;
string text = userName;
Process cmd = new Process();
cmd.StartInfo.FileName = "cmd.exe";
cmd.StartInfo.Arguments = "/c net user";
cmd.StartInfo.UseShellExecute = false;
cmd.StartInfo.RedirectStandardOutput = true;
cmd.StartInfo.RedirectStandardError = true;
cmd.Start();
//* Read the output (or the error)
string output = cmd.StandardOutput.ReadToEnd();
string err = cmd.StandardError.ReadToEnd();
Console.WriteLine("Output: "+output );
Console.WriteLine("Err: "+err);
cmd.WaitForExit();
Console.ReadKey();
EDIT
USER_INFO_2
The USER_INFO_2 structure contains information about a user account, including the account name, password data, privilege level, the path to the user's home directory, and other user-related network statistics.
usri2_acct_expires
Specifies a DWORD value that indicates when the account expires. This value is stored as the number of seconds elapsed since 00:00:00, January 1, 1970, GMT. A value of TIMEQ_FOREVER indicates that the account never expires.
from USER_INFO_2 used with NetUserGetInfo.
Declared in Lmaccess.h; include Lm.h.
It is really poor practise to shell to a user commands to do simple things.
Your essential problem and your very silly conclusion that user management isn't available is wrong.
If another program can do something then so can your program.
If you don't know how you look at the API calls it calls (open it in notepad and look - they are alltogether in the file). .NET has it's own WMI class.
C# and C++ and C all have access to WMI. The command line for what you are doing via WMI is
wmic path Win32_Account get /format:list
wmic path Win32_Group get /format:list
wmic path Win32_GroupInDomain get /format:list
wmic path Win32_GroupUser get /format:list
wmic path Win32_SystemAccount get /format:list
wmic path Win32_SystemUsers get /format:list
wmic path Win32_UserAccount get /format:list
For Help
wmic /?
wmic UserAccount /?
wmic useraccount get /?
wmic useraccount set /?
wmic useraccount call /?
The same thing in vbscript is
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_UserAccount")
For Each objItem in colItems
msgbox objitem.Caption & " " & objItem.Description
Next
A C++ example from WDK
#include "querysink.h"
int main(int argc, char **argv)
{
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the local root\cimv2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&pSvc
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system.
// The IWbemService::ExecQueryAsync method will call
// the QuerySink::Indicate method when it receives a result
// and the QuerySink::Indicate method will display the OS name
QuerySink* pResponseSink = new QuerySink();
hres = pSvc->ExecQueryAsync(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_OperatingSystem"),
WBEM_FLAG_BIDIRECTIONAL,
NULL,
pResponseSink);
if (FAILED(hres))
{
cout << "Query for operating system name failed."
<< " Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
pResponseSink->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 7: -------------------------------------------------
// Wait to get the data from the query in step 6 -----------
Sleep(500);
pSvc->CancelAsyncCall(pResponseSink);
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 0; // Program successfully completed.
}
The following code is the header file code for the QuerySink class. The QuerySink class is used in the previous code.
// QuerySink.h
#ifndef QUERYSINK_H
#define QUERYSINK_H
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")
class QuerySink : public IWbemObjectSink
{
LONG m_lRef;
bool bDone;
CRITICAL_SECTION threadLock; // for thread safety
public:
QuerySink() { m_lRef = 0; bDone = false;
InitializeCriticalSection(&threadLock); }
~QuerySink() { bDone = true;
DeleteCriticalSection(&threadLock); }
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void** ppv);
virtual HRESULT STDMETHODCALLTYPE Indicate(
LONG lObjectCount,
IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
);
virtual HRESULT STDMETHODCALLTYPE SetStatus(
/* [in] */ LONG lFlags,
/* [in] */ HRESULT hResult,
/* [in] */ BSTR strParam,
/* [in] */ IWbemClassObject __RPC_FAR *pObjParam
);
bool IsDone();
};
#endif // end of QuerySink.h
The following code is an implementation of the QuerySink class:
// QuerySink.cpp
#include "querysink.h"
ULONG QuerySink::AddRef()
{
return InterlockedIncrement(&m_lRef);
}
ULONG QuerySink::Release()
{
LONG lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
HRESULT QuerySink::QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == IID_IWbemObjectSink)
{
*ppv = (IWbemObjectSink *) this;
AddRef();
return WBEM_S_NO_ERROR;
}
else return E_NOINTERFACE;
}
HRESULT QuerySink::Indicate(long lObjectCount,
IWbemClassObject **apObjArray)
{
HRESULT hres = S_OK;
for (int i = 0; i < lObjectCount; i++)
{
VARIANT varName;
hres = apObjArray[i]->Get(_bstr_t(L"Name"),
0, &varName, 0, 0);
if (FAILED(hres))
{
cout << "Failed to get the data from the query"
<< " Error code = 0x"
<< hex << hres << endl;
return WBEM_E_FAILED; // Program has failed.
}
printf("Name: %ls\n", V_BSTR(&varName));
}
return WBEM_S_NO_ERROR;
}
HRESULT QuerySink::SetStatus(
/* [in] */ LONG lFlags,
/* [in] */ HRESULT hResult,
/* [in] */ BSTR strParam,
/* [in] */ IWbemClassObject __RPC_FAR *pObjParam
)
{
if(lFlags == WBEM_STATUS_COMPLETE)
{
printf("Call complete.\n");
EnterCriticalSection(&threadLock);
bDone = true;
LeaveCriticalSection(&threadLock);
}
else if(lFlags == WBEM_STATUS_PROGRESS)
{
printf("Call in progress.\n");
}
return WBEM_S_NO_ERROR;
}
bool QuerySink::IsDone()
{
bool done = true;
EnterCriticalSection(&threadLock);
done = bDone;
LeaveCriticalSection(&threadLock);
return done;
} // end of QuerySink.cpp
Related
This question already has answers here:
Getting Serial Port Information
(8 answers)
Closed 14 days ago.
In our company for testing purposes, we use many serial COM port devices. These devices are transferred daily between several PC and whenever we add a new serial device, we have to manualy learn on all computers.
Currently I am working on code, for automatic COM port device detection. My question is, how to list also device ID in c++ or c# next to the active COM port list?
With serial number, I will be able to automatically detect on what port device is on on each PC.
Also for FTDI, Silicon Labs,..USB to RS232 converter I can manualy set device SN. Solution need to work on windows 7 or newer.
For example:
My code:
static void Main(string[] args)
{
// Get a list of serial port names.
string[] ports = SerialPort.GetPortNames();
Console.WriteLine("The following serial ports were found:");
// Display each port name to the console.
foreach (string port in ports)
{
Console.WriteLine(port);
}
Console.ReadLine();
}
You can use ManagementObjectSearcher for this. You'll need to add System.Management as a reference.
using System;
using System.Management; // need to add System.Management to your project references
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * From Win32_USBHub");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("USB device");
Console.WriteLine("Caption: {0}", queryObj["Caption"]);
Console.WriteLine("Description: {0}", queryObj["Description"]);
Console.WriteLine("DeviceID: {0}", queryObj["DeviceID"]);
Console.WriteLine("Name: {0}", queryObj["Name"]);
Console.WriteLine("PNPDeviceID: {0}", queryObj["PNPDeviceID"]);
}
I found a working code check this post Getting Serial Port Information
Code:
using System.Management;
using Microsoft.Win32;
using (ManagementClass i_Entity = new ManagementClass("Win32_PnPEntity"))
{
foreach (ManagementObject i_Inst in i_Entity.GetInstances())
{
Object o_Guid = i_Inst.GetPropertyValue("ClassGuid");
if (o_Guid == null || o_Guid.ToString().ToUpper() != "{4D36E978-E325-11CE-BFC1-08002BE10318}")
continue; // Skip all devices except device class "PORTS"
String s_Caption = i_Inst.GetPropertyValue("Caption") .ToString();
String s_Manufact = i_Inst.GetPropertyValue("Manufacturer").ToString();
String s_DeviceID = i_Inst.GetPropertyValue("PnpDeviceID") .ToString();
String s_RegPath = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Enum\\" + s_DeviceID + "\\Device Parameters";
String s_PortName = Registry.GetValue(s_RegPath, "PortName", "").ToString();
int s32_Pos = s_Caption.IndexOf(" (COM");
if (s32_Pos > 0) // remove COM port from description
s_Caption = s_Caption.Substring(0, s32_Pos);
Console.WriteLine("Port Name: " + s_PortName);
Console.WriteLine("Description: " + s_Caption);
Console.WriteLine("Manufacturer: " + s_Manufact);
Console.WriteLine("Device ID: " + s_DeviceID);
Console.WriteLine("-----------------------------------");
}
Console.ReadLine();
}
Console output:
Port Name: COM29
Description: CDC Interface (Virtual COM Port) for USB Debug
Manufacturer: GHI Electronics, LLC
Device ID: USB\VID_1B9F&PID_F003&MI_01\6&3009671A&0&0001
-----------------------------------
Port Name: COM28
Description: Teensy USB Serial
Manufacturer: PJRC.COM, LLC.
Device ID: USB\VID_16C0&PID_0483\1256310
-----------------------------------
Port Name: COM25
Description: USB-SERIAL CH340
Manufacturer: wch.cn
Device ID: USB\VID_1A86&PID_7523\5&2499667D&0&3
-----------------------------------
Port Name: COM26
Description: Prolific USB-to-Serial Comm Port
Manufacturer: Prolific
Device ID: USB\VID_067B&PID_2303\5&2499667D&0&4
-----------------------------------
Port Name: COM1
Description: Comunications Port
Manufacturer: (Standard port types)
Device ID: ACPI\PNP0501\1
-----------------------------------
Port Name: COM999
Description: EPSON TM Virtual Port Driver
Manufacturer: EPSON
Device ID: ROOT\PORTS\0000
-----------------------------------
Port Name: COM20
Description: EPSON COM Emulation USB Port
Manufacturer: EPSON
Device ID: ROOT\PORTS\0001
-----------------------------------
Port Name: COM8
Description: Standard Serial over Bluetooth link
Manufacturer: Microsoft
Device ID: BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&000F\8&3ADBDF90&0&001DA568988B_C00000000
-----------------------------------
Port Name: COM9
Description: Standard Serial over Bluetooth link
Manufacturer: Microsoft
Device ID: BTHENUM\{00001101-0000-1000-8000-00805F9B34FB}_LOCALMFG&0000\8&3ADBDF90&0&000000000000_00000002
-----------------------------------
Port Name: COM30
Description: Arduino Uno
Manufacturer: Arduino LLC (www.arduino.cc)
Device ID: USB\VID_2341&PID_0001\74132343530351F03132
-----------------------------------
If I understood your questions correctly, the C++ code below should help.
Edit: added support for friendly name (which includes COM number).
On my system I get this result, which seems to match your screenshots:
installed serial devices:
Dev. Id: PCI\VEN_8086&DEV_9D3D&SUBSYS_506D17AA&REV_21\3&11583659&1&B3
friendly name: Intel(R) Active Management Technology - SOL (COM3)
Dev. Id: FTDIBUS\VID_0403+PID_6001+A916RGSAA\0000
friendly name: USB Serial Port (COM4)
device interfaces:
Interface: \\?\PCI#VEN_8086&DEV_9D3D&SUBSYS_506D17AA&REV_21#3&11583659&1&B3#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
Interface: \\?\FTDIBUS#VID_0403+PID_6001+A916RGSAA#0000#{86e0d1e0-8089-11d0-9ce4-08003e301f73}
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <initguid.h>
#include <windows.h>
#include <devpkey.h>
#include <cfgmgr32.h>
auto splitListAsString(const std::wstring_view& s)
{
std::vector<std::wstring> l;
for (size_t pos{ 0 }; s.at(pos) != 0;) {
auto newpos = s.find(L'\0', pos);
l.emplace_back(s.substr(pos, newpos - pos));
pos = newpos + 1;
}
return l;
}
int main()
{
wchar_t guid_str[] = L"{4D36E978-E325-11CE-BFC1-08002BE10318}";
auto propKey{ DEVPKEY_Device_FriendlyName };
ULONG len{};
auto res = CM_Get_Device_ID_List_Size(&len, guid_str, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT);
if (res != CR_SUCCESS) {
std::cerr << "error num " << res << " occured\n";
return -1;
}
std::wstring devIds(len,'\0');
res = CM_Get_Device_ID_ListW(guid_str, devIds.data(), len, CM_GETIDLIST_FILTER_CLASS | CM_GETIDLIST_FILTER_PRESENT);
std::wcout << L"installed serial devices:\n";
for (auto& e : splitListAsString(devIds)) {
std::wcout << L"Dev. Id: " << e << L'\n';
DEVINST devInst{};
res = CM_Locate_DevInstW(&devInst, e.data(), CM_LOCATE_DEVNODE_NORMAL);
if (res != CR_SUCCESS) {
std::cerr << "error locating device " << res << " occured\n";
continue;
}
DEVPROPTYPE devpropt{};
CM_Get_DevNode_PropertyW(devInst, &propKey, &devpropt, nullptr, &len, 0);
if (res!=CR_BUFFER_SMALL && res != CR_SUCCESS) {
std::cerr << "error " << res << "\n";
continue;
//return -1;
}
auto buffer = std::make_unique<BYTE[]>(len);
res = CM_Get_DevNode_PropertyW(devInst, &propKey, &devpropt, buffer.get(), &len, 0);
if (devpropt == DEVPROP_TYPE_STRING) {
const auto val = reinterpret_cast<wchar_t*>(buffer.get());
std::wcout << L"friendly name: " << val << L"\n\n";
}
}
auto guid_comport_interface{ GUID_DEVINTERFACE_COMPORT };
res = CM_Get_Device_Interface_List_SizeW(&len, &guid_comport_interface, nullptr, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (res != CR_SUCCESS) {
std::cerr << "error num " << res << " occured\n";
return -1;
}
std::wstring devInterfaces(len, '\0');
res = CM_Get_Device_Interface_ListW(&guid_comport_interface, nullptr, devInterfaces.data(), len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
std::wcout << L"\ndevice interfaces:\n";
for (const auto& e : splitListAsString(devInterfaces)) {
std::wcout << L"Interface: " << e << '\n';
}
}
I am trying to write buffer data to SCSI device, but when I fire the deviceIoControl call, I get the windows error code 183. The error code indicates File already exits but I am not able to understand the error with respect to the IOCTL call.
The CreateFile function passed as i was able to get the appropriate device Handle as shown below.
int devHandle = DeviceIoControlHelper.CreateFile(
deviceName,
DeviceIoControlHelper.GENERIC_READ | DeviceIoControlHelper.GENERIC_WRITE,
DeviceIoControlHelper.FILE_SHARE_READ | DeviceIoControlHelper.FILE_SHARE_WRITE,
IntPtr.Zero,
DeviceIoControlHelper.OPEN_EXISTING,
0,
IntPtr.Zero);
However, when i try to write the buffer I get the error :
dwReturned = 0;
int b = DeviceIoControlHelper.DeviceIoControl(
devHandle,
DeviceIoControlHelper.IOCTL_SCSI_PASS_THROUGH,
inpBuffer,
(uint)Marshal.SizeOf(info),
inpBuffer, //out pDriveLayout,
(uint)Marshal.SizeOf(info), //(uint)Marshal.SizeOf(typeof(DRIVE_LAYOUT_INFORMATION_EX)),
out dwReturned,
IntPtr.Zero);
Log.Write(Log.TraceLevel_5,"ExecuteDeviceIoControl return pass_through scsistatus = " + info.spt.ScsiStatus);
if (b == 0)
{
int win_err = this.GetWindowsErrorCode();
Log.Write(Log.TraceLevel_5, "ExecuteDeviceIoControl failed, error = " + win_err);
LogMyTrace.Write(LogMyTrace.TraceLevel_5, "ExecuteDeviceIoControl failed, error = " + win_err);
this.CloseOpenHandle(devHandle);
Marshal.FreeHGlobal(inpBuffer);
throw new Exception("DeviceIoControl failed " + deviceName +
" is '" + deviceName + "'. Windows Err is " + win_err);
}
The error code is 183.
Please provide some inputs as why this error is being caused and the soultion.
I am using Windows 2008 R2 (x64) and it is the iSCSI path.
I need to write a small service that runs an application (with gui, e.g. calc.exe) on the logon-screen.
I already found this question (and answer):
Running a process at the Windows 7 Welcome Screen
please read the code-comments if you don't understand how this works:
// grab the winlogon process
Process winLogon = null;
foreach (Process p in Process.GetProcesses())
{
if (p.ProcessName.Contains("winlogon"))
{
winLogon = p;
break;
}
}
// grab the winlogon's token
IntPtr userToken = IntPtr.Zero;
if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken))
{
log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
}
// create a new token
IntPtr newToken = IntPtr.Zero;
SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
// duplicate the winlogon token to the new token
if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenImpersonation, out newToken))
{
log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
}
TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
tokPrivs.PrivilegeCount = 1;
LUID seDebugNameValue = new LUID();
if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue))
{
log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
}
tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// escalate the new token's privileges
if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero))
{
log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
}
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "Winsta0\\Winlogon";
// start the process using the new token
if (!CreateProcessAsUser(newToken, "calc.exe", null, ref tokenAttributes, ref threadAttributes,
true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,
"C:\\Windows\\System32", ref si, out pi))
{
log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
}
Process _p = Process.GetProcessById(pi.dwProcessId);
if (_p != null)
{
log("Process " + _p.Id + " Name " + _p.ProcessName);
}
else
{
log("Process not found");
}
It works with Windows 7. With XP I get error 1349 ERROR_BAD_TOKEN_TYPE while calling CreateProcessAsUser (MSDN: The type of the token is inappropriate for its attempted use.).
How to realize this in Windows XP? It does not have to be the code from above but it should work as a service (with system-account?).
Thanks for your support
Fluxer
This definitely has to do with privilege issues (Windows Vista and 7 have notable changes in security). Rather than trying to get token of winlogon.exe and impersonating it, try getting user token via WTSQueryUserToken like this:
WTSQueryUserToken (WTSGetActiveConsoleSessionId(), out userToken);
Replace the OpenProcessToken line which is used to get the token with the above statement.
Your new code should be like this:
// if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | //TOKEN_DUPLICATE, out userToken))
// {
// log("ERROR: OpenProcessToken returned false - " + //Marshal.GetLastWin32Error());
// }
WTSQueryUserToken (WTSGetActiveConsoleSessionId(), out userToken);
Do your dll import like this:
[DllImport("Kernel32.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.U4)]
public static extern int WTSGetActiveConsoleSessionId ( );
You need only replace the part I commented out with this code.
I need to write a small tool that runs on every userdesktop or, if no one is logged in, directly on logon screen. Maybe a service with a form starting?
I already found this question (and answer):
Running a process at the Windows 7 Welcome Screen
// grab the winlogon process
Process winLogon = null;
foreach (Process p in Process.GetProcesses()) {
if (p.ProcessName.Contains("winlogon")) {
winLogon = p;
break;
}
}
// grab the winlogon's token
IntPtr userToken = IntPtr.Zero;
if (!OpenProcessToken(winLogon.Handle, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, out userToken)) {
log("ERROR: OpenProcessToken returned false - " + Marshal.GetLastWin32Error());
}
// create a new token
IntPtr newToken = IntPtr.Zero;
SECURITY_ATTRIBUTES tokenAttributes = new SECURITY_ATTRIBUTES();
tokenAttributes.nLength = Marshal.SizeOf(tokenAttributes);
SECURITY_ATTRIBUTES threadAttributes = new SECURITY_ATTRIBUTES();
threadAttributes.nLength = Marshal.SizeOf(threadAttributes);
// duplicate the winlogon token to the new token
if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
TOKEN_TYPE.TokenImpersonation, out newToken)) {
log("ERROR: DuplicateTokenEx returned false - " + Marshal.GetLastWin32Error());
}
TOKEN_PRIVILEGES tokPrivs = new TOKEN_PRIVILEGES();
tokPrivs.PrivilegeCount = 1;
LUID seDebugNameValue = new LUID();
if (!LookupPrivilegeValue(null, SE_DEBUG_NAME, out seDebugNameValue)) {
log("ERROR: LookupPrivilegeValue returned false - " + Marshal.GetLastWin32Error());
}
tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// escalate the new token's privileges
if (!AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero)) {
log("ERROR: AdjustTokenPrivileges returned false - " + Marshal.GetLastWin32Error());
}
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "Winsta0\\Winlogon";
// start the process using the new token
if (!CreateProcessAsUser(newToken, process, process, ref tokenAttributes, ref threadAttributes,
true, (uint)CreateProcessFlags.CREATE_NEW_CONSOLE | (uint)CreateProcessFlags.INHERIT_CALLER_PRIORITY, IntPtr.Zero,
logInfoDir, ref si, out pi)) {
log("ERROR: CreateProcessAsUser returned false - " + Marshal.GetLastWin32Error());
}
Process _p = Process.GetProcessById(pi.dwProcessId);
if (_p != null) {
log("Process " + _p.Id + " Name " + _p.ProcessName);
} else {
log("Process not found");
}
But there are no dll-imports explained, so i can't build this.
Thanks for your effort
Fluxer
Runnnig this code getting this error, could any one help figure out what is going on here?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace LSATest
{
class Program
{
public static List<string> listBox1 = new List<string>();
static void Main(string[] args)
{
DateTime systime = new DateTime(1601, 1, 1, 0, 0, 0, 0); //win32 systemdate
UInt64 count;
IntPtr luidPtr = IntPtr.Zero;
LSAClass.LsaEnumerateLogonSessions(out count, out luidPtr); //gets an array of pointers to LUIDs
IntPtr iter = luidPtr; //set the pointer to the start of the array
for (ulong i = 0; i < count; i++) //for each pointer in the array
{
IntPtr sessionData;
LSAClass.LsaGetLogonSessionData(iter, out sessionData);
LSAClass.SECURITY_LOGON_SESSION_DATA data = (LSAClass.SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(sessionData, typeof(LSAClass.SECURITY_LOGON_SESSION_DATA));
//if we have a valid logon
if (data.PSiD != IntPtr.Zero)
{
//get the security identifier for further use
System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(data.PSiD);
//extract some useful information from the session data struct
string username = Marshal.PtrToStringUni(data.Username.buffer).Trim(); //get the account username
string domain = Marshal.PtrToStringUni(data.LoginDomain.buffer).Trim(); //domain for this account
string authpackage = Marshal.PtrToStringUni(data.AuthenticationPackage.buffer).Trim(); //authentication package
LSAClass.SECURITY_LOGON_TYPE secType = (LSAClass.SECURITY_LOGON_TYPE)data.LogonType;
DateTime time = systime.AddTicks((long)data.LoginTime); //get the datetime the session was logged in
//do something with the extracted data, ie, add to a display control....
listBox1.Add("User: " + username + " *** Domain: " + domain + " *** Login Type: (" + data.LogonType + ") " + secType.ToString() + " *** Login Time: " + time.ToLocalTime().ToString());
}
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(LSAClass.LUID))); //move the pointer forward
LSAClass.LsaFreeReturnBuffer(sessionData); //free the SECURITY_LOGON_SESSION_DATA memory in the struct
}
LSAClass.LsaFreeReturnBuffer(luidPtr); //free the array of LUIDs
}
}
}
The error is because sessionData is null. That's because LsaGetLogonSessionData is failing. To diagnose this further, you need to start paying attention to the return value of the API functions and checking error codes in case of failure. That's your next step.
As an aside, you have declared the count variable incorrectly. In the C header file it is declared as ULONG. That's an unsigned 32 bit integer which makes it uint in C#. I've not checked anything more than this.