I'm working on an application that is a sound editor for a video game.
This application has an option to test the sounds in the game, to do this I use Process.Start with some special parameters to open the game .exe.
The game uses OutputDebugStringA() to print debug messages. The idea is to have a multiline textbox inside this form to show the debug messages only of the game process.
I've been doing some research and I know about the existence of DebugView from sysinternals, but is not useful for this case, I've also found DbMon.NET in code project, but for some reason causes the game to run very slow (with DebugView doesn't happen).
Any proposal?
Try this, you can specify the process ID inside the do while loop before printing it in the textbox.
public partial class Form1 : Form
{
private readonly IntPtr _bufferReadyEvent;
private readonly IntPtr _dataReadyEvent;
private readonly IntPtr _mapping;
private readonly IntPtr _file;
private const int WaitTimeout = 500;
private const uint WAIT_OBJECT_0 = 0;
private readonly Thread _reader;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, FileMapProtection flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccess dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
[Flags]
private enum FileMapAccess
{
FileMapRead = 0x0004,
}
[Flags]
private enum FileMapProtection
{
PageReadWrite = 0x04,
}
public Form1()
{
InitializeComponent();
_bufferReadyEvent = CreateEvent(IntPtr.Zero, false, false, "DBWIN_BUFFER_READY");
if (_bufferReadyEvent == IntPtr.Zero)
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
_dataReadyEvent = CreateEvent(IntPtr.Zero, false, false, "DBWIN_DATA_READY");
if (_dataReadyEvent == IntPtr.Zero)
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
_mapping = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, FileMapProtection.PageReadWrite, 0, 4096, "DBWIN_BUFFER");
if (_mapping == IntPtr.Zero)
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
_file = MapViewOfFile(_mapping, FileMapAccess.FileMapRead, 0, 0, 1024);
if (_file == IntPtr.Zero)
Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
_reader = new Thread(Read)
{
IsBackground = true
};
_reader.Start();
}
private void Read()
{
try
{
do
{
SetEvent(_bufferReadyEvent);
uint wait = WaitForSingleObject(_dataReadyEvent, WaitTimeout);
if (wait == WAIT_OBJECT_0) // we don't care about other return values
{
int pid = Marshal.ReadInt32(_file);
string text = Marshal.PtrToStringAnsi(new IntPtr(_file.ToInt32() + Marshal.SizeOf(typeof(int)))).TrimEnd(null);
if (string.IsNullOrEmpty(text))
{
continue;
}
Textbox_DebugConsole.Invoke((MethodInvoker)delegate
{
Textbox_DebugConsole.AppendText(string.Format("{0} - {1}", pid, text + Environment.NewLine));
});
}
}
while (true);
}
catch (Exception e)
{
MessageBox.Show(e.Message, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
Related
when I try to inject a DLL with C# into a java process (Minecraft specifically) I get an error "ERROR 6", I've been googling this error but couldnt find a solution.
The program is run in administrator mode, and it isnt trying to load from an admin path.
My code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace Launcher
{
internal class InjectionUtils
{
public struct CTX_PROCS
{
public Process[] procs;
}
private const int PROCESS_CREATE_THREAD = 2;
private const int PROCESS_QUERY_INFORMATION = 1024;
private const int PROCESS_VM_OPERATION = 8;
private const int PROCESS_VM_WRITE = 32;
private const int PROCESS_VM_READ = 16;
private const uint MEM_COMMIT = 4096u;
private const uint MEM_RESERVE = 8192u;
private const uint PAGE_READWRITE = 4u;
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint option);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
private static extern uint GetLastError();
private void MBox(string text)
{
InjectionUtils.MessageBox(IntPtr.Zero, text, "", 0u);
}
public void DoInjection(string dllPath)
{
InjectionUtils.CTX_PROCS cTX_PROCS = this.FindProcessByNameAndTitle("javaw", "Minecraft 1.7");
this.InjectDynamicLib(dllPath, cTX_PROCS.procs[0].Id);
}
public InjectionUtils.CTX_PROCS FindProcessByNameAndTitle(string processName, string title)
{
InjectionUtils.CTX_PROCS result = default(InjectionUtils.CTX_PROCS);
List<Process> list = new List<Process>();
Process[] processes = Process.GetProcesses();
for (int i = 0; i < processes.Length; i++)
{
Process process = processes[i];
if (process.ProcessName.Equals(processName) && process.MainWindowTitle.Contains(title))
{
list.Add(process);
}
}
result.procs = list.ToArray();
return result;
}
public void eject()
{
}
private bool InjectDynamicLib(string dllPath, int pId)
{
bool result = false;
IntPtr intPtr = InjectionUtils.OpenProcess(1082, false, pId);
uint num = (uint)((dllPath.Length + 1) * Marshal.SizeOf(typeof(char)));
if (intPtr != IntPtr.Zero)
{
IntPtr procAddress = InjectionUtils.GetProcAddress(InjectionUtils.GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr intPtr2 = InjectionUtils.VirtualAllocEx(intPtr, IntPtr.Zero, num, 12288u, 4u);
UIntPtr uIntPtr;
// Write path of dll to remote process
if (InjectionUtils.WriteProcessMemory(intPtr, intPtr2, Encoding.Default.GetBytes(dllPath), num, out uIntPtr))
{
InjectionUtils.CloseHandle(InjectionUtils.CreateRemoteThread(intPtr, IntPtr.Zero, 0u, procAddress, intPtr2, 0u, IntPtr.Zero));
result = true;
}
InjectionUtils.CloseHandle(intPtr);
}
if (InjectionUtils.GetLastError() != 0u)
{
this.MBox("ERROR " + InjectionUtils.GetLastError());
}
return result;
}
If anyone could help I would appreciate it dearly :)
Thanks in advance :D
From MSDN Error Code List:
ERROR_INVALID_HANDLE
0x6
Before you call GetLastError() you call CloseHandle() twice, once on the thread handle and on the process handle.
To find the problem just check the returns from OpenProcess() and CreateRemoteThread() and compare against the info you find in the functions documentation on MSDN.
For a simple no frills C# injector, this is the code I like to use which includes some error checking and uses the managed Process::Dispose() method instead of calling the native CloseHandle() at least on the process handle:
public static bool InjectDLL(string dllpath, string procname)
{
Process[] procs = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(procname));
if (procs.Length == 0)
{
return false;
}
Process proc = procs[0];
if (proc.Handle != IntPtr.Zero)
{
IntPtr loc = VirtualAllocEx(proc.Handle, IntPtr.Zero, MAX_PATH, AllocationType.Commit | AllocationType.Reserve,
MemoryProtection.ReadWrite);
if (loc.Equals(0))
{
return false;
}
IntPtr bytesRead = IntPtr.Zero;
bool result = WriteProcessMemory(proc.Handle, loc, dllpath.ToCharArray(), dllpath.Length, out bytesRead);
if (!result || bytesRead.Equals(0))
{
return false;
}
IntPtr loadlibAddy = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr hThread = CreateRemoteThread(proc.Handle, IntPtr.Zero, 0, loadlibAddy, loc, 0, out _);
if (!hThread.Equals(0))
{
CloseHandle(hThread);
}
else return false;
}
else return false;
//this will CloseHandle automatically using the managed method
proc.Dispose();
return true;
}
Make sure to run as admin and pinvoke any missing definitions.
Given a Windows 8 (.1) computer with the following power options:
And screensaver configured as:
The goal is to programmatically turn the display on and "kill" the screensaver (so that it will be re-activated after idle time).
(Note, that according to the settings it's possible that only the screensaver is on, or, that the display is off completely after the screensaver was on for about one minute).
What i have tried is:
SendMessage(HWND_Broadcast, WM_SysCommand, SC_MONITORPOWER, (LPARAM) - 1);
in combination with
// From Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
// "How to force a screen saver to close once started
// in Windows NT, Windows 2000, and Windows Server 2003"
public static void KillScreenSaver()
{
IntPtr hDesktop = OpenDesktop("Screen-saver", 0, false, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
{
if (!EnumDesktopWindows(hDesktop, KillScreenSaverFunc, IntPtr.Zero) || !CloseDesktop(hDesktop))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
TerminateWindow(GetForegroundWindow());
}
}
private static bool KillScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
{
if (IsWindowVisible(hWnd))
{
TerminateWindow(hWnd);
}
return true;
}
private static void TerminateWindow(IntPtr hWnd)
{
if (!PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
And
public static void ActivateScreensaver()
{
SetScreenSaverActive(TRUE);
}
private static void SetScreenSaverActive(uint active)
{
IntPtr nullVar = IntPtr.Zero;
// Ignoring error since ERROR_OPERATION_IN_PROGRESS is expected.
// Methode is called to reset timer and to prevent possible errors as mentioned in Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, active, ref nullVar, SPIF_SENDWININICHANGE);
}
Which worked, partially. However, the display was turned off immediately again (as a matter of fact, most times one couldn't even see that the monitor was turned again apart from the power-led going "steady on" instead of "flashing on off = power safe" for a short time).
So i guess i'm missing a crucial part of the picture like reliably resetting system idle timer to make sure the screen is not immediately being turned off, again.
Edit: According to my investigations SetThreadExecutionState(ES_DISPLAY_REQUIRED) seems to do the trick in regards to resetting the idle-timer. However, i still don't know the correct sequence of calls. I'll self-answer when i figure it out...
The following code will:
interrupt a running screensaver
turn on a turned-off screen ("turn off" as mentioned in power options)
private static readonly ILog Log = LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
public void TurnOnScreenAndInterruptScreensaver()
{
TryTurnOnScreenAndResetDisplayIdleTimer();
TryInterruptScreensaver();
}
/// <summary>
/// Moves the mouse which turns on a turned-off screen and also resets the
/// display idle timer, which is key, because otherwise the
/// screen would be turned off again immediately.
/// </summary>
private static void TryTurnOnScreenAndResetDisplayIdleTimer()
{
var input = new SendInputNativeMethods.Input {
type = SendInputNativeMethods.SendInputEventType.InputMouse, };
try
{
SendInputNativeMethods.SendInput(input);
}
catch (Win32Exception exception)
{
Log.Error("Could not send mouse move input to turn on display", exception);
}
}
private static void TryInterruptScreensaver()
{
try
{
if (ScreensaverNativeMethods.GetScreenSaverRunning())
{
ScreensaverNativeMethods.KillScreenSaver();
}
// activate screen saver again so that after idle-"timeout" it shows again
ScreensaverNativeMethods.ActivateScreensaver();
}
catch (Win32Exception exception)
{
Log.Error("Screensaver could not be deactivated", exception);
}
}
SendInputNativeMethods:
public static class SendInputNativeMethods
{
public static void SendInput(params Input[] inputs)
{
if (SendInput((uint)inputs.Length, inputs, Marshal.SizeOf<Input>())
!= (uint)inputs.Length)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint SendInput(
uint nInputs,
[MarshalAs(UnmanagedType.LPArray), In] Input[] pInputs,
int cbSize);
[StructLayout(LayoutKind.Sequential)]
public struct Input
{
public SendInputEventType type;
public MouseKeybdhardwareInputUnion mkhi;
}
[StructLayout(LayoutKind.Explicit)]
public struct MouseKeybdhardwareInputUnion
{
[FieldOffset(0)]
public MouseInputData mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
public int uMsg;
public short wParamL;
public short wParamH;
}
public struct MouseInputData
{
public int dx;
public int dy;
public uint mouseData;
public MouseEventFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[Flags]
public enum MouseEventFlags : uint
{
MOUSEEVENTF_MOVE = 0x0001,
MOUSEEVENTF_LEFTDOWN = 0x0002,
MOUSEEVENTF_LEFTUP = 0x0004,
MOUSEEVENTF_RIGHTDOWN = 0x0008,
MOUSEEVENTF_RIGHTUP = 0x0010,
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
MOUSEEVENTF_MIDDLEUP = 0x0040,
MOUSEEVENTF_XDOWN = 0x0080,
MOUSEEVENTF_XUP = 0x0100,
MOUSEEVENTF_WHEEL = 0x0800,
MOUSEEVENTF_VIRTUALDESK = 0x4000,
MOUSEEVENTF_ABSOLUTE = 0x8000
}
public enum SendInputEventType : int
{
InputMouse,
InputKeyboard,
InputHardware
}
ScreensaverNativeMethods:
internal static class ScreensaverNativeMethods
{
private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
private const int SPI_SETSCREENSAVEACTIVE = 0x0011;
private const int SPIF_SENDWININICHANGE = 0x0002;
private const uint DESKTOP_WRITEOBJECTS = 0x0080;
private const uint DESKTOP_READOBJECTS = 0x0001;
private const int WM_CLOSE = 0x0010;
private const int TRUE = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SystemParametersInfo(
uint uiAction,
uint uiParam,
ref IntPtr pvParam,
uint fWinIni);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool PostMessage(
IntPtr hWnd,
uint msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr OpenDesktop(
string lpszDesktop,
uint dwFlags,
[In, MarshalAs(UnmanagedType.Bool)]bool fInherit,
uint dwDesiredAccess);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseDesktop(IntPtr hDesktop);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumDesktopWindows(
IntPtr hDesktop,
EnumDesktopWindowsProc callback,
IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetForegroundWindow();
private delegate bool EnumDesktopWindowsProc(IntPtr hDesktop, IntPtr lParam);
public static bool GetScreenSaverRunning()
{
IntPtr isRunning = IntPtr.Zero;
if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref isRunning, 0))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return isRunning != IntPtr.Zero;
}
public static void ActivateScreensaver()
{
SetScreenSaverActive(TRUE);
}
private static void SetScreenSaverActive(uint active)
{
IntPtr nullVar = IntPtr.Zero;
// Ignoring error since ERROR_OPERATION_IN_PROGRESS is expected.
// Methode is called to reset timer and to prevent possible errors
// as mentioned in Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
SystemParametersInfo(
SPI_SETSCREENSAVEACTIVE,
active,
ref nullVar,
SPIF_SENDWININICHANGE);
}
// From Microsoft's Knowledge Base article #140723:
// http://support.microsoft.com/kb/140723
// "How to force a screen saver to close once started
// in Windows NT, Windows 2000, and Windows Server 2003"
public static void KillScreenSaver()
{
IntPtr hDesktop = OpenDesktop(
"Screen-saver",
0,
false,
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (hDesktop != IntPtr.Zero)
{
if (!EnumDesktopWindows(hDesktop, KillScreenSaverFunc, IntPtr.Zero)
|| !CloseDesktop(hDesktop))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
else
{
TerminateWindow(GetForegroundWindow());
}
}
private static bool KillScreenSaverFunc(IntPtr hWnd, IntPtr lParam)
{
if (IsWindowVisible(hWnd))
{
TerminateWindow(hWnd);
}
return true;
}
private static void TerminateWindow(IntPtr hWnd)
{
if (!PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
In my situation i want to load a custom .net assembly into a running .net process's domain, for example Windows Explorer, What i have tried already is just injecting the assembly to explorer.exe but that doesn't seem to work for no obvious reason.
Injector Code:
public class CodeInjector
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(uint dwDesiredAccess, int bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttribute, IntPtr dwStackSize, IntPtr lpStartAddress,
IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
private static CodeInjector _instance;
public static CodeInjector GetInstance
{
get { return _instance ?? (_instance = new CodeInjector()); }
}
public InjectionResult Inject(string sProcName, string sDllPath)
{
if (!File.Exists(sDllPath))
{
return InjectionResult.DllNotFound;
}
var procs = Process.GetProcesses();
var procId = (from t in procs where t.ProcessName == sProcName select (uint)t.Id).FirstOrDefault();
if (procId == 0)
{
return InjectionResult.ProcessNotFound;
}
if (!Inject(procId, sDllPath))
{
return InjectionResult.InjectionFailed;
}
return InjectionResult.InjectionSucceed;
}
private static bool Inject(uint pToBeInjected, string sDllPath)
{
var hndProc = OpenProcess((0x2 | 0x8 | 0x10 | 0x20 | 0x400), 1, pToBeInjected);
if (hndProc == IntPtr.Zero)
{
return false;
}
var lpLlAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (lpLlAddress == IntPtr.Zero)
{
return false;
}
var lpAddress = VirtualAllocEx(hndProc, (IntPtr)null, (IntPtr)sDllPath.Length, (0x1000 | 0x2000), 0X40);
if (lpAddress == IntPtr.Zero)
{
return false;
}
var bytes = Encoding.ASCII.GetBytes(sDllPath);
if (WriteProcessMemory(hndProc, lpAddress, bytes, (uint)bytes.Length, 0) == 0)
{
return false;
}
if (CreateRemoteThread(hndProc, (IntPtr)null, IntPtr.Zero, lpLlAddress, lpAddress, 0, (IntPtr)null) == IntPtr.Zero)
{
return false;
}
CloseHandle(hndProc);
return true;
}
}
As another option you can use existing library - ManagedInjector - https://github.com/cplotts/snoopwpf/tree/master/ManagedInjector . There is a tool snoopwpf that can show details of any WPF process, and it uses process injection for that. I used it some time ago and it worked well.
You need to build it, add to your project as reference and call like this:
Injector.Launch(someProcess.MainWindowHandle,
typeof(Loader).Assembly.Location,
typeof(Loader).FullName,
"Inject");
Loader is name of type that will be loaded into process and Inject is a static method that will be executed. In my case i had:
public class Loader
{
public static void Inject()
{
// i did CBT Hook on main window here
// and also displayed sample message box for debugging purposes
MessageBox.Show("Hello from another process");
}
}
That ManagedInjector is written in Managed C++ code. Basically it hooks own unmanaged C++ method as MessageHookProc and it will start specified assembly after injection and run specified method. It should work fine for both managed and unmanaged programs. In my case i used it for unmanaged program.
UPDATE
I tested it locally and it successfully injects my message box into explorer process under Windows 8.1 x64. I compiled ManagedInjector64-4.0 and my sample console project also has x64 in platform selection. Here is my working code:
class Program
{
static void Main(string[] args)
{
var proc = Process.GetProcessesByName("explorer").FirstOrDefault();
Injector.Launch(proc.MainWindowHandle, typeof(Loader).Assembly.Location, typeof(Loader).FullName, "Inject");
}
}
public class Loader
{
public static void Inject()
{
MessageBox.Show("Hello");
Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("Hello again");
Thread.Sleep(5000);
MessageBox.Show("Hello again again");
});
}
}
I wanted to run an GUI application via a service application in C#, so i have tried with System.Diagnostics.Process.Start() method as below:
if (KillTask("notepad") == false)
{
//ProcessStartInfo _ProcessStartInfo = new ProcessStartInfo(#"C:\WINDOWS\system32\notepad.exe");
//_ProcessStartInfo.UseShellExecute = false;
//_ProcessStartInfo.RedirectStandardError = true;
//_ProcessStartInfo.RedirectStandardInput = true;
//_ProcessStartInfo.RedirectStandardOutput = true;
//_ProcessStartInfo.CreateNoWindow = true;
//_ProcessStartInfo.ErrorDialog = false;
//_ProcessStartInfo.WindowStyle = ProcessWindowStyle.Maximized;
//System.Diagnostics.Process.Start(_ProcessStartInfo);
System.Diagnostics.Process.Start("notepad.exe");
}
The problem is that Notepad goes run but with no UI and you can see it in the task manager but no GUI Instance of Notepad was shown.
I've also tried with the ProcessStartInfo() class as you can see as remarked code, but the problem still exists.
Have you tried setting the username and password property of ProcessStartInfo to a user that is currently logged on? The problem is that the system user does not have a gui available.
Hello what happens is that the process runs in an environment without a session,
It is necessary to have a user session, start the process and send the GUI to the graphical desktop of this started session.
This is verified since when you start your Process you get an ID and it can be checked in TaskManager/Details/
Sort by PID and you will see that service started but in SYSTEM.
It is necessary to create a class to obtain the logged in User and send the GUI to that desktop
class ProcessExtensions.cs
public static class ProcessExtensions
{
#region Win32 Constants
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
#endregion
#region DllImports
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
IntPtr lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern int WTSEnumerateSessions(
IntPtr hServer,
int Reserved,
int Version,
ref IntPtr ppSessionInfo,
ref int pCount);
#endregion
#region Win32 Structs
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public readonly UInt32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public readonly String pWinStationName;
public readonly WTS_CONNECTSTATE_CLASS State;
}
#endregion
// Gets the user token from the currently active session
private static bool GetSessionUserToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)
{
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetSessionUserToken(ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
}
Then you just call your process as follows
public static void run()
{
ProcessExtensions.StartProcessAsCurrentUser(#"calc.exe");
}
Next Recompile you Service // Install // Start //
More Info:
How to start a process from windows service into currently logged in user's session
Credits: #murrayju
https://github.com/murrayju/CreateProcessAsUser
I'm interested in how plausible it would be to capture Win32 debug traces system-wide in the way DebugView does. I'm not interested in kernel messages thankfully, so I don't need any help there. This needs to be in C#, but I'm happy with unmanaged/unsafe if necessary.
Is there any global hook I can get or am I setting off down a difficult road?
I'm really not sure where the best place is to start on this.
I finally got there. It took some serious googling, but I found an article which helped...
All kudos go to Chritian Birkl for his rather excellent Code Project DbMon.NET - A simple .NET OutputDebugString capturer.
The code is pretty heavy-going, but here it is:
using System;
using System.Threading;
using System.Runtime.InteropServices;
public delegate void OnOutputDebugStringHandler(int pid, string text);
public sealed class DebugMonitor
{
private DebugMonitor()
{
;
}
#region Win32 API Imports
[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_DESCRIPTOR
{
public byte revision;
public byte size;
public short control;
public IntPtr owner;
public IntPtr group;
public IntPtr sacl;
public IntPtr dacl;
}
[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
private enum PageProtection : uint
{
NoAccess = 0x01,
Readonly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
Guard = 0x100,
NoCache = 0x200,
WriteCombine = 0x400,
}
private const int WAIT_OBJECT_0 = 0;
private const uint INFINITE = 0xFFFFFFFF;
private const int ERROR_ALREADY_EXISTS = 183;
private const uint SECURITY_DESCRIPTOR_REVISION = 1;
private const uint SECTION_MAP_READ = 0x0004;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
uint dwNumberOfBytesToMap);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool InitializeSecurityDescriptor(ref SECURITY_DESCRIPTOR sd, uint dwRevision);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetSecurityDescriptorDacl(ref SECURITY_DESCRIPTOR sd, bool daclPresent, IntPtr dacl, bool daclDefaulted);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES sa, bool bManualReset, bool bInitialState, string lpName);
[DllImport("kernel32.dll")]
private static extern bool PulseEvent(IntPtr hEvent);
[DllImport("kernel32.dll")]
private static extern bool SetEvent(IntPtr hEvent);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFileMapping(IntPtr hFile,
ref SECURITY_ATTRIBUTES lpFileMappingAttributes, PageProtection flProtect, uint dwMaximumSizeHigh,
uint dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hHandle);
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
private static extern Int32 WaitForSingleObject(IntPtr handle, uint milliseconds);
#endregion
public static event OnOutputDebugStringHandler OnOutputDebugString;
private static IntPtr m_AckEvent = IntPtr.Zero;
private static IntPtr m_ReadyEvent = IntPtr.Zero;
private static IntPtr m_SharedFile = IntPtr.Zero;
private static IntPtr m_SharedMem = IntPtr.Zero;
private static Thread m_Capturer = null;
private static object m_SyncRoot = new object();
private static Mutex m_Mutex = null;
public static void Start()
{
lock (m_SyncRoot)
{
if (m_Capturer != null)
throw new ApplicationException("This DebugMonitor is already started.");
if (Environment.OSVersion.ToString().IndexOf("Microsoft") == -1)
throw new NotSupportedException("This DebugMonitor is only supported on Microsoft operating systems.");
bool createdNew = false;
m_Mutex = new Mutex(false, typeof(DebugMonitor).Namespace, out createdNew);
if (!createdNew)
throw new ApplicationException("There is already an instance of 'DbMon.NET' running.");
SECURITY_DESCRIPTOR sd = new SECURITY_DESCRIPTOR();
if (!InitializeSecurityDescriptor(ref sd, SECURITY_DESCRIPTOR_REVISION))
{
throw CreateApplicationException("Failed to initializes the security descriptor.");
}
if (!SetSecurityDescriptorDacl(ref sd, true, IntPtr.Zero, false))
{
throw CreateApplicationException("Failed to initializes the security descriptor");
}
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
m_AckEvent = CreateEvent(ref sa, false, false, "DBWIN_BUFFER_READY");
if (m_AckEvent == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create event 'DBWIN_BUFFER_READY'");
}
m_ReadyEvent = CreateEvent(ref sa, false, false, "DBWIN_DATA_READY");
if (m_ReadyEvent == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create event 'DBWIN_DATA_READY'");
}
m_SharedFile = CreateFileMapping(new IntPtr(-1), ref sa, PageProtection.ReadWrite, 0, 4096, "DBWIN_BUFFER");
if (m_SharedFile == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create a file mapping to slot 'DBWIN_BUFFER'");
}
m_SharedMem = MapViewOfFile(m_SharedFile, SECTION_MAP_READ, 0, 0, 512);
if (m_SharedMem == IntPtr.Zero)
{
throw CreateApplicationException("Failed to create a mapping view for slot 'DBWIN_BUFFER'");
}
m_Capturer = new Thread(new ThreadStart(Capture));
m_Capturer.Start();
}
}
private static void Capture()
{
try
{
IntPtr pString = new IntPtr(
m_SharedMem.ToInt32() + Marshal.SizeOf(typeof(int))
);
while (true)
{
SetEvent(m_AckEvent);
int ret = WaitForSingleObject(m_ReadyEvent, INFINITE);
if (m_Capturer == null)
break;
if (ret == WAIT_OBJECT_0)
{
FireOnOutputDebugString(
Marshal.ReadInt32(m_SharedMem),
Marshal.PtrToStringAnsi(pString));
}
}
}
catch
{
throw;
}
finally
{
Dispose();
}
}
private static void FireOnOutputDebugString(int pid, string text)
{
if (OnOutputDebugString == null)
return;
#if !DEBUG
try
{
#endif
OnOutputDebugString(pid, text);
#if !DEBUG
}
catch (Exception ex)
{
Console.WriteLine("An 'OnOutputDebugString' handler failed to execute: " + ex.ToString());
}
#endif
}
private static void Dispose()
{
if (m_AckEvent != IntPtr.Zero)
{
if (!CloseHandle(m_AckEvent))
{
throw CreateApplicationException("Failed to close handle for 'AckEvent'");
}
m_AckEvent = IntPtr.Zero;
}
if (m_ReadyEvent != IntPtr.Zero)
{
if (!CloseHandle(m_ReadyEvent))
{
throw CreateApplicationException("Failed to close handle for 'ReadyEvent'");
}
m_ReadyEvent = IntPtr.Zero;
}
if (m_SharedFile != IntPtr.Zero)
{
if (!CloseHandle(m_SharedFile))
{
throw CreateApplicationException("Failed to close handle for 'SharedFile'");
}
m_SharedFile = IntPtr.Zero;
}
if (m_SharedMem != IntPtr.Zero)
{
if (!UnmapViewOfFile(m_SharedMem))
{
throw CreateApplicationException("Failed to unmap view for slot 'DBWIN_BUFFER'");
}
m_SharedMem = IntPtr.Zero;
}
if (m_Mutex != null)
{
m_Mutex.Close();
m_Mutex = null;
}
}
public static void Stop()
{
lock (m_SyncRoot)
{
if (m_Capturer == null)
throw new ObjectDisposedException("DebugMonitor", "This DebugMonitor is not running.");
m_Capturer = null;
PulseEvent(m_ReadyEvent);
while (m_AckEvent != IntPtr.Zero)
;
}
}
private static ApplicationException CreateApplicationException(string text)
{
if (text == null || text.Length < 1)
throw new ArgumentNullException("text", "'text' may not be empty or null.");
return new ApplicationException(string.Format("{0}. Last Win32 Error was {1}",
text, Marshal.GetLastWin32Error()));
}
}
I wrote a tool that does it. It's not 100% equivalent to DebugView, but it has some other nice features (like the ability to colorize traces) :-).
It's available here: TraceSpy
Since it's 100% open source, it contains C# code that demonstrates how to do it.
I would start with the TraceListener class, although I'm not sure if this can be used to capture Win32 debug tracing.