Send key from C# to interrupt Fortran process - c#

I need to communicate with a command line Fortran app using a c# wrapper. The Fortran process is started using the following c# code.
var process = new Process();
process.StartInfo = new ProcessStartInfo(pathToFortranExe)
{
WorkingDirectory = directory,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true
};
...
//listen for prompts from the Fortran program
//and send replies using standardInput as follows
process.StandardInput.WriteLine(data);
When the Fortran program is waiting for user input on the command line I can successfully send messages using the above code.
Now here is the problem. The Fortran program uses long running analysis loops which can be interrupted by sending keys such as Esq or Q. I've been told this interrupt feature is implemented in the Fortran code using the Intel Fortran command PEEKCHARQQ. When I try and trigger these keys from c# using StandardInput they are ignored by the Fortran program. To send these interrupt signals I use:
char key = 'q'
process.StandardInput.Write(key);
//Note that StandardInput.AutoFlush==true
I've also tried SendMessage via pinvoke, but again no luck so far:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_KEYDOWN = 0x0100;
private const UInt32 WM_KEYUP = 0x0101;
public static void SendKey(Process process, char key)
{
var keyCode = (IntPtr)key;
var hWnd = process.Handle;
SendMessage(hWnd, WM_KEYDOWN, keyCode, IntPtr.Zero);
SendMessage(hWnd, WM_KEYUP, keyCode, IntPtr.Zero);
}
So the question is: Are there any other ways to place keys into the keyboard buffer such that they might be picked up PEEKCHARQQ in the Fortran process? Or anything else that I might be missing here?
Update 1:
I've also tried WriteConsoleInput, but I don't think I have the right handle:
var keyCode = (short)key;
var hWnd = process.Handle;
INPUT_RECORD[] lpBuffer = new INPUT_RECORD[1];
lpBuffer[0].KeyEvent.wVirtualKeyCode = keyCode;
int nLength = lpBuffer.Length;
int lpNumberOfEventsWritten;
if (!WriteConsoleInput(
hWnd,
lpBuffer,
nLength,
out lpNumberOfEventsWritten))
{
//this results error code 6: Invalid handle
Console.WriteLine("Error: {0}", GetLastError());
}

You can write to the console input buffer for the child process using a handle to the CONIN$ device with the WriteConsoleInput API.
Two Fortran programs below to demonstrate. The program for the child process:
PROGRAM peek_a_boo
USE IFCORE
IMPLICIT NONE
CHARACTER(*), PARAMETER :: fmt = "('Press any key!')"
PRINT fmt
DO WHILE (.NOT. PEEKCHARQQ())
CALL SLEEPQQ(2000)
PRINT fmt
END DO
END PROGRAM peek_a_boo
The program that shows the Windows API calls. You can translate this to whichever language floats your boat.
PROGRAM peek_parent
USE IFWIN
IMPLICIT NONE
INTEGER(BOOL) :: api_result
INTEGER(HANDLE) :: console_input
TYPE(T_STARTUPINFO) :: startup_info
TYPE(T_PROCESS_INFORMATION) :: process_info
TYPE(T_INPUT_RECORD) :: input_record
INTEGER(DWORD) :: events_written
INTEGER(DWORD) :: wait_event
! Create the child process.
CALL ZeroMemory(LOC(startup_info), SIZEOF(startup_info))
startup_info%cb = SIZEOF(startup_info)
api_result = CreateProcess( &
'peek_a_boo.exe', &
NULL, & ! command line.
NULL, & ! process security attributes.
NULL, & ! thread security attributes.
.FALSE., & ! inherit handles.
0_DWORD, &
NULL, & ! environment.
NULL, & ! current directory.
startup_info, &
process_info )
IF (api_result == 0) THEN
ERROR STOP 'Couldn''t start it :('
END IF
api_result = CloseHandle(process_info%hThread)
! Let the child run for a bit.
CALL SLEEPQQ(5000)
! Get a handle to our console input buffer.
console_input = CreateFile( &
'CONIN$', &
GENERIC_WRITE, &
IANY([FILE_SHARE_READ, FILE_SHARE_WRITE]), &
NULL, & ! security attrs
OPEN_EXISTING, &
0_DWORD, & ! attributes
NULL ) ! template file
IF (console_input == 0) THEN
ERROR STOP 'Couldn''t open it :('
END IF
! Poke something into the buffer.
input_record%EventType = KEY_EVENT
input_record%KeyEvent%bKeyDown = .TRUE.
input_record%KeyEvent%wRepeatCount = 1
input_record%KeyEvent%wVirtualKeyCode = INT(Z'51', WORD)
input_record%KeyEvent%wVirtualScanCode = INT(Z'51', WORD)
input_record%KeyEvent%AsciiChar = 'Q'
input_record%KeyEvent%dwControlKeyState = 0
api_result = WriteConsoleInput( &
console_input, &
input_record, &
1, &
LOC(events_written) )
! Wait for the child to terminate.
wait_event = WaitForSingleObject(process_info%hProcess, INFINITE)
api_result = CloseHandle(console_input)
api_result = CloseHandle(process_info%hProcess)
END PROGRAM peek_parent

Related

C# console application stdin/stdout redirection

I have an interesting (read: frustrating) problem kicking off a console application from a C# WPF app and redirecting its stdin and stdout.
It is mostly up and working but I seem to end up not getting some data from stdout as soon as I start redirecting stdin.
I'll clarify with an example. If I don't set hStdInput in the STARTUPINFO structure, when I start the child process I receive the following:
MongoDB shell version: 2.2.0
connecting to: test
local:PRIMARY>
Once I set hStdInput however, I just get this:
MongoDB shell version: 2.2.0
connecting to: test
I know that the BackgroundWorker processing stdout is still functioning because if I send something to the process on stdin, it responds accordingly.
use TestDB
switched to db TestDB
So, this is how I create the process:
_processInfo = new ProcessInfo();
bool ok = false;
SECURITY_ATTRIBUTES sattr = new SECURITY_ATTRIBUTES();
sattr.bInheritHandle = 1;
unsafe
{
sattr.lpSecurityDescriptor = null;
}
sattr.nLength = Marshal.SizeOf(sattr);
IntPtr hWrite;
ok = CreatePipe(out _hReadStdOut, out hWrite, ref sattr, 0);
ok = SetHandleInformation(_hReadStdOut, HANDLE_FLAGS.INHERIT, 0);
IntPtr hRead;
ok = CreatePipe(out hRead, out _hWriteStdIn, ref sattr, 0);
ok = SetHandleInformation(_hWriteStdIn, HANDLE_FLAGS.INHERIT, 0);
var startInfo = new StartupInfo
{
dwFlags = 0x0001 | 0x0100,
wShowWindow = 0,
hStdOutput = hWrite,
hStdError = hWrite,
hStdInput = hRead // If this is IntPtr.Zero, I get everything from stdout
};
SECURITY_ATTRIBUTES pSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
SECURITY_ATTRIBUTES tSec = new SECURITY_ATTRIBUTES();
tSec.nLength = Marshal.SizeOf(tSec);
unsafe
{
ok = CreateProcess(
null,
pathToExeAndArgs,
ref pSec,
ref tSec,
true,
0,
IntPtr.Zero,
null,
ref startInfo,
out _processInfo);
}
I have a BackgroundWorker processing stdout on DoWork which reads the pipe like so:
success = ReadFile(
_hReadStdOut,
bufPtr,
1024,
&read,
IntPtr.Zero);
I'm not using the .Net Process class because it didn't obtain data from stdout until the console application sent a newline, so I didn't get the prompt back in that case either.
Any help with this greatly appreciated.
Cheers.
I suspect the following explains what you have observed:
When you don't define hStdInput the child process uses the standard input device attached to the console. The child process detects that standard input is an interactive console device and writes a prompt.
When you do define hStdInput the child process detects that the standard input is a pipe and so neglects to write a prompt. After all, what's the point of prompting a non-interactive input device?
The child process will use GetFileType(GetStdHandle(STD_INPUT_HANDLE)) to detect what type of device is attached to the standard input. A value of FILE_TYPE_CHAR indicates a console. When you attach a pipe to the standard input then the standard input file type will be FILE_TYPE_PIPE.
My conclusion is that everything is working as designed and intended.

application which is elevated needs to get non-elevated mapped drives list

I know that a .NET application which is run as elevated cannot see the mapped drives of the user. I also know there is a registry hack to fix this (which involves a reboot).
I would like to create a different solution to this problem. Our application must run elevated and it relies heavily on the mapped drives that the user has created. I'd like to detect the mapped drives the user has, and map a similar bunch from the elevated application.
So the question is: how do I detect the mapped drives of the 'normal' user from an elevated application?
If a mapped drive was mapped persistently (checkbox "reconnect at logon" enabled) then you can find it in the user's registry hive:
HKEY_CURRENT_USER\Network\<drive letter>
That key has a value RemotePath containing the UNC path.
I've got a few solutions to this problem now. Reading the registry is ok but you won't find drives which are created by your domain profile or other temporary mapped drives.
One solution I have tested, which works, is to use this code to create an un-elevated process. I wrote a simple GetMappedDrives.exe app which writes the mapped drives to a file. The elevated app can then read the file to obtain the un-elevated mapped drives.
Another solution might be to use an un-elevated wrapper around your elevated application. The un-elevated wrapper would call GetMappedDrives.exe first then run the elevated app. Problem is that you won't know about changes to the mapped drives until you restart the app.
A third solution might be applicable if you happen have an un-elevated process running as part of your suite of applications. Just bounce a network message off the un-elevated app telling it to call GetMappedDrives.exe.
GetMappedDrives c# code...
static void Main(string[] args)
{
//if (args.GetLength(0) != 1)
//{
// Debug.Assert(false);
// Console.WriteLine("You must supply the target file path");
// Environment.Exit(1);
//}
//String strTargetPath = args[0];
String strTargetPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + #"\MyCompany\MappedDrives.csv";
FileStream fs = new FileStream(strTargetPath, FileMode.Create, FileAccess.Write);
StreamWriter writer = new StreamWriter(fs);
DriveInfo[] allDrives = DriveInfo.GetDrives();
foreach (DriveInfo dInfo in allDrives)
{
if (dInfo.DriveType == DriveType.Network || dInfo.DriveType == DriveType.Removable)
{
String s = String.Format("{0},{1},{2},{3},{4}\n", dInfo.Name, dInfo.VolumeLabel, dInfo.DriveType, dInfo.TotalSize, dInfo.AvailableFreeSpace);
writer.Write(s);
}
}
writer.Close();
}
TestGetMappedDrives c++ code. Run this in an elevated command prompt...
#include "stdafx.h"
#include "windows.h"
//#ifndef SECURITY_MANDATORY_HIGH_RID
// #define SECURITY_MANDATORY_UNTRUSTED_RID (0x00000000L)
// #define SECURITY_MANDATORY_LOW_RID (0x00001000L)
// #define SECURITY_MANDATORY_MEDIUM_RID (0x00002000L)
// #define SECURITY_MANDATORY_HIGH_RID (0x00003000L)
// #define SECURITY_MANDATORY_SYSTEM_RID (0x00004000L)
// #define SECURITY_MANDATORY_PROTECTED_PROCESS_RID (0x00005000L)
//#endif
//#ifndef TokenIntegrityLevel
// #define TokenIntegrityLevel ((TOKEN_INFORMATION_CLASS)25)
//#endif
//#ifndef TOKEN_MANDATORY_LABEL
// typedef struct
// {
// SID_AND_ATTRIBUTES Label;
// } TOKEN_MANDATORY_LABEL;
//#endif
typedef BOOL (WINAPI *defCreateProcessWithTokenW)
(HANDLE,DWORD,LPCWSTR,LPWSTR,DWORD,LPVOID,LPCWSTR,LPSTARTUPINFOW,LPPROCESS_INFORMATION);
// Writes Integration Level of the process with the given ID into pu32_ProcessIL
// returns Win32 API error or 0 if succeeded
DWORD GetProcessIL(DWORD u32_PID, DWORD* pu32_ProcessIL)
{
*pu32_ProcessIL = 0;
HANDLE h_Process = 0;
HANDLE h_Token = 0;
DWORD u32_Size = 0;
BYTE* pu8_Count = 0;
DWORD* pu32_ProcIL = 0;
TOKEN_MANDATORY_LABEL* pk_Label = 0;
h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_PID);
if (!h_Process)
goto _CleanUp;
if (!OpenProcessToken(h_Process, TOKEN_QUERY, &h_Token))
goto _CleanUp;
if (!GetTokenInformation(h_Token, TokenIntegrityLevel, NULL, 0, &u32_Size) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER)
goto _CleanUp;
pk_Label = (TOKEN_MANDATORY_LABEL*) HeapAlloc(GetProcessHeap(), 0, u32_Size);
if (!pk_Label)
goto _CleanUp;
if (!GetTokenInformation(h_Token, TokenIntegrityLevel, pk_Label, u32_Size, &u32_Size))
goto _CleanUp;
pu8_Count = GetSidSubAuthorityCount(pk_Label->Label.Sid);
if (!pu8_Count)
goto _CleanUp;
pu32_ProcIL = GetSidSubAuthority(pk_Label->Label.Sid, *pu8_Count-1);
if (!pu32_ProcIL)
goto _CleanUp;
*pu32_ProcessIL = *pu32_ProcIL;
SetLastError(ERROR_SUCCESS);
_CleanUp:
DWORD u32_Error = GetLastError();
if (pk_Label) HeapFree(GetProcessHeap(), 0, pk_Label);
if (h_Token) CloseHandle(h_Token);
if (h_Process) CloseHandle(h_Process);
return u32_Error;
}
// Creates a new process u16_Path with the integration level of the Explorer process (MEDIUM IL)
// If you need this function in a service you must replace FindWindow() with another API to find Explorer process
// The parent process of the new process will be svchost.exe if this EXE was run "As Administrator"
// returns Win32 API error or 0 if succeeded
DWORD CreateProcessMediumIL(WCHAR* u16_Path, WCHAR* u16_CmdLine)
{
HANDLE h_Process = 0;
HANDLE h_Token = 0;
HANDLE h_Token2 = 0;
PROCESS_INFORMATION k_ProcInfo = {0};
STARTUPINFOW k_StartupInfo = {0};
BOOL b_UseToken = FALSE;
// Detect Windows Vista, 2008, Windows 7 and higher
if (GetProcAddress(GetModuleHandleA("Kernel32"), "GetProductInfo"))
{
DWORD u32_CurIL;
DWORD u32_Err = GetProcessIL(GetCurrentProcessId(), &u32_CurIL);
if (u32_Err)
return u32_Err;
if (u32_CurIL > SECURITY_MANDATORY_MEDIUM_RID)
b_UseToken = TRUE;
}
// Create the process normally (before Windows Vista or if current process runs with a medium IL)
if (!b_UseToken)
{
if (!CreateProcessW(u16_Path, u16_CmdLine, 0, 0, FALSE, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
return GetLastError();
CloseHandle(k_ProcInfo.hThread);
CloseHandle(k_ProcInfo.hProcess);
return ERROR_SUCCESS;
}
defCreateProcessWithTokenW f_CreateProcessWithTokenW =
(defCreateProcessWithTokenW) GetProcAddress(GetModuleHandleA("Advapi32"), "CreateProcessWithTokenW");
if (!f_CreateProcessWithTokenW) // This will never happen on Vista!
return ERROR_INVALID_FUNCTION;
HWND h_Progman = ::GetShellWindow();
DWORD u32_ExplorerPID = 0;
GetWindowThreadProcessId(h_Progman, &u32_ExplorerPID);
// ATTENTION:
// If UAC is turned OFF all processes run with SECURITY_MANDATORY_HIGH_RID, also Explorer!
// But this does not matter because to start the new process without UAC no elevation is required.
h_Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, u32_ExplorerPID);
if (!h_Process)
goto _CleanUp;
if (!OpenProcessToken(h_Process, TOKEN_DUPLICATE, &h_Token))
goto _CleanUp;
if (!DuplicateTokenEx(h_Token, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &h_Token2))
goto _CleanUp;
if (!f_CreateProcessWithTokenW(h_Token2, 0, u16_Path, u16_CmdLine, 0, 0, 0, &k_StartupInfo, &k_ProcInfo))
goto _CleanUp;
SetLastError(ERROR_SUCCESS);
_CleanUp:
DWORD u32_Error = GetLastError();
if (h_Token) CloseHandle(h_Token);
if (h_Token2) CloseHandle(h_Token2);
if (h_Process) CloseHandle(h_Process);
CloseHandle(k_ProcInfo.hThread);
CloseHandle(k_ProcInfo.hProcess);
return u32_Error;
}
int _tmain(int argc, _TCHAR* argv[])
{
DWORD u32_Err = CreateProcessMediumIL(L"C:\\src\\bin\\release\\GetMappedDrives.exe", NULL);
printf("CreateProcessMediumIL() exited with error %d\r\n", u32_Err);
Sleep(2000);
return 0;
}

Create windows console application that mirrors a valid application (Malware Class)

I am teaching a class and am trying to show how you shouldn't trust applications on your system blindly.
The first demo is to run netstat and output the list of sockets connected.
I have created another application with the same name, but it omits the IP passed into arg[0] from the display. The console output is the same. The goal being that you can have a file named the correct name, but not necessarily legitimate. (Obviously hashes won't match)
The next demo is something like tasklist, though I have having trouble getting the "Session Name" from processes.GetProcesses. If I run tasklist on the demo machine (XP) I can't find the value associated with it. Also, is there a easy way to sort the list, as tasklist sorts on PID. I am new to C# so bear with me.
Thanks!
Process[] procs = Process.GetProcesses();
foreach (Process proc in procs)
{
Console.WriteLine(image.PadRight(17) + pid.PadLeft(5) + sname.PadRight(16) + mem.PadLeft(12));
}
So the goal is to replicate tasklist (basic functionality) or another windows command line app to show that a real malware author could replicate all of it.
Update:
All the students will be running in on their own XP VM so I can't really have them connect somewhere else. Being on XP also eliminates the UAC issue.
Here's a way to just use any command-line app and inspect the text (and modify it if you wish):
var exeName = #"tasklist.exe";
var arguments = "";
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo(exeName)
{
Arguments = arguments,
CreateNoWindow = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardError = true,
RedirectStandardOutput = true,
},
};
process.OutputDataReceived +=
(sender, e) => Console.WriteLine("received output: {0}", e.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
You can get the image name from the Process.Modules list (first element), the pid from the Process.Id property, and the memory from Process.Working set; but, you'll have to pinvoke WTSQuerySessionInformation to get the session name. For example:
foreach(Process p in Process.GetProcesses())
{
IntPtr buffer;
uint bytesReturned;
WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, (uint) p.SessionId, WTS_INFO_CLASS.WTSWinStationName, out buffer, out bytesReturned);
var sessionName = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
string moduleName = p.ProcessName;
try
{
moduleName = p.Modules[0].ModuleName;
}
catch(Exception ex)
{
ex = ex;
}
Console.WriteLine(String.Format("{0,-17} {1,5} {2,-16} {3,12} {4,12} K", moduleName, p.Id, sessionName, p.SessionId, (p.WorkingSet64 / 1024).ToString("n0")));
}
Which assumes the following is declared in the class:
enum WTS_INFO_CLASS
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType,
WTSIdleTime,
WTSLogonTime,
WTSIncomingBytes,
WTSOutgoingBytes,
WTSIncomingFrames,
WTSOutgoingFrames,
WTSClientInfo,
WTSSessionInfo
};
[DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
static extern bool WTSQuerySessionInformation(
IntPtr hServer,
uint sessionId,
WTS_INFO_CLASS wtsInfoClass,
out IntPtr ppBuffer,
out uint pBytesReturned
);

Open Directory Using CreateFile

I am trying to utilize the CreateFile function to access directory information. I am recieving a win32 error code of 5 however, which means Access Denied. Please advise.
CreateFile(path, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
This is the call being made, and as noted in the documentation the 'FILE_FLAG_BACKUP_SEMANTICS' is being used. The DLL import seems to be working fine and looks like the following:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateFile(string filename,
uint desiredAccess,
uint sharedMode,
IntPtr securityAttributes,
uint creationDisposition,
uint flagsAndAttributes,
IntPtr templateFile);
Update: I need to obtain the handle to a directory so i can use GetFileInformationByHandle() and extract the unique id. This method currently works with files, it is not working with directories currently.
Update: The X for this question is i need a unique identifier of a directory that is something other than its absolute path. It needs to remain the same even if directory is moved or renamed. .NET does not provide any unique identifiers as just mentioned, it can only be accomplished by using win32
First of all you should include manifest in your application to be sure that it runs under Administrator privileges. Then you should enable SE_BACKUP_NAME privilege using AdjustTokenPrivileges API. Then I would recommend you to use FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE flags as the sharedMode. Now you should be able to use CreateFile to open the directory handle and use GetFileInformationByHandle to get BY_HANDLE_FILE_INFORMATION.
UPDATED: Probably the following simple demo program can help you
#include <windows.h>
#include <tchar.h>
int _tmain()
{
HANDLE hAccessToken = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
__try {
LUID luidPrivilege;
DWORD dwErrorCode;
BY_HANDLE_FILE_INFORMATION fiFileInfo;
// -----------------------------------------------------
// first of all we need anable SE_BACKUP_NAME privilege
// -----------------------------------------------------
if (!OpenProcessToken (GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hAccessToken))
__leave;
if (LookupPrivilegeValue (NULL, SE_BACKUP_NAME, &luidPrivilege)) {
TOKEN_PRIVILEGES tpPrivileges;
tpPrivileges.PrivilegeCount = 1;
tpPrivileges.Privileges[0].Luid = luidPrivilege;
tpPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges (hAccessToken, FALSE, &tpPrivileges,
0, NULL, NULL);
if ((dwErrorCode = GetLastError ()) != ERROR_SUCCESS)
__leave;
}
else
__leave;
// -----------------------------------------------------
// now one can open directory and get
// -----------------------------------------------------
hFile = CreateFile (TEXT("C:\\"),
0, //GENERIC_READ,
0, //FILE_SHARE_READ, //FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
__leave;
if (!GetFileInformationByHandle (hFile, &fiFileInfo))
__leave;
_tprintf(TEXT("VolumeSerialNumber: 0x%08X\n"), fiFileInfo.dwVolumeSerialNumber);
_tprintf(TEXT("FileIndex: 0x%08X%08X\n"), fiFileInfo.nFileIndexHigh, fiFileInfo.nFileIndexLow);
}
__finally {
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle (hFile);
if (hAccessToken != NULL)
CloseHandle (hAccessToken);
}
return 0;
}
The program opens C:\ directory and display Volume Serial Number and File Index which identify the directory on the NTFS. To make program shorter I removed all error messages (see __leave statements). Like I already mention before you should use requireAdministrator as "UAC Execution Level" (see "Manifest File" part of the Linker settings). The above code is tested and it work at me. You can reproduce the same code in C#.
UPDATED: Probably the following simple demo program can help you...
I tried to SetFileTime() but it was wrong. I have modified so:
hFile = CreateFile( TEXT("C:\\MyDirectory"),
// 0, //GENERIC_READ,
GENERIC_READ | GENERIC_WRITE,
// 0, //FILE_SHARE_READ, //FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
and it is ok. Thanks. Andre.

C# STARTUPINFO Flags to show UI for a process started from a Service in XP

I'm launching a process from a windows service in XP, I'm just launching the process not trying to interact with it. The process starts but the UI does not show. I believe I need to set some flags in STARTUPINFO to make process visible, and hoping someone could show how and what flags to set.
sPath = #"C:\Windows\notepad.exe";
string Message = string.Empty;
// Variables
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
STARTUPINFO startInfo = new STARTUPINFO();
Boolean bResult = false;
IntPtr hToken = IntPtr.Zero;
try
{
// Logon user
bResult = LogonUser(
"Test",
"VirtualXP-23639",
"test",
LogonType.LOGON32_LOGON_INTERACTIVE,
LogonProvider.LOGON32_PROVIDER_DEFAULT,
out hToken
);
if (!bResult) { throw new Exception("Logon error #" + Marshal.GetLastWin32Error()); }
// Create process
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
bResult = CreateProcessAsUser(
hToken,
null,
sPath,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
null,
ref startInfo,
out processInfo
);
if (!bResult)
{
Message = "Failed to Create Process on Desktop/Console. Code=" + Marshal.GetLastWin32Error().ToString();
Logging.LogError(Ascension.CM.Common.Enums.ApplicationModuleEnums.Service, Message, "Ascension.CM.ServiceWorker.ProcessLauncher.XpLaunchDesktopProcess", null);
}
}
finally
{
// Close all handles
CloseHandle(hToken);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
}
You'll at least need to allow the service to interact with the desktop, so in services.msc, click on your serivce an go to properties, then logon and select allow to interact with desktop..
I would suggest that you use the Process class in the .net framework.
Process.Start("notepad.exe")
This should have your desired effect.
Thanks guys, but I've found a solution.
I ended up using WTSQueryUserToken to get the current logged in user and then used DuplicateTokenEx to get a token that I used with CreateProcessAsUser to start the process.
For XP use session id 0 and for win7 use WTSGetActiveConsoleSessionId to get the current session Id.
This works fine with out having to use the "Allow to interact with Desktop" property.
Thanks

Categories