Translate WinApi Crypt to the .NET Framework System.Security.Cryptography - c#

I would like to ask for a help with refactoring following C# code.
The target is to remove all external WinApi call and replace them with methods from System.Security.Cryptography namespace.
private static IntPtr GenerateKey(IntPtr hCryptProv, byte[] keyData)
{
var hHash = IntPtr.Zero;
Win32.CryptCreateHash(hCryptProv, Win32.CALG_MD5, IntPtr.Zero, 0, ref hHash);
var len = (uint)keyData.Length;
Win32.CryptHashData(hHash, keyData, len, 0);
var hKey = IntPtr.Zero;
Win32.CryptDeriveKey(hCryptProv, Win32.CALG_3DES, hHash, 0, ref hKey);
if (hHash != IntPtr.Zero) Win32.CryptDestroyHash(hHash);
return hKey;
}
public static byte[] Encrypt(byte[] dataToEncrypt)
{
var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
var size = (uint)dataToEncrypt.Length;
var buffer = new byte[size * 2];
Array.Copy(dataToEncrypt, 0, buffer, 0, size);
var hCryptProv = IntPtr.Zero;
bool gotcsp = Win32.CryptAcquireContext(ref hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
if (!gotcsp)
{
Win32.CryptAcquireContext(ref hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_NEWKEYSET | Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
}
if (hCryptProv == IntPtr.Zero) return null;
var hKey = GenerateKey(hCryptProv, keyData);
Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, buffer, ref size, size*2);
var encryptedData = new byte[size];
Array.Copy(buffer, 0, encryptedData, 0, size);
if (hKey != IntPtr.Zero) Win32.CryptDestroyKey(hKey);
if (hCryptProv != IntPtr.Zero) Win32.CryptReleaseContext(hCryptProv, 0);
return encryptedData;
}
/// <summary>
/// WinAPI Imports
/// </summary>
internal class Win32
{
public const uint PROV_RSA_FULL = 1;
public const uint NTE_BAD_KEYSET = 0x80090016;
public const uint CRYPT_NEWKEYSET = 0x00000008;
public const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
public const uint CRYPT_MACHINE_KEYSET = 0x00000020;
public const uint ALG_CLASS_HASH = (4 << 13);
public const uint ALG_SID_MD5 = 3;
public const uint CALG_MD5 = (ALG_CLASS_HASH | ALG_SID_MD5);
public const uint ALG_CLASS_DATA_ENCRYPT = (3 << 13);
public const uint ALG_TYPE_BLOCK = (3 << 9);
public const uint ALG_SID_3DES = 3;
public const uint CALG_3DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptAcquireContext(ref IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDestroyKey(IntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptCreateHash(IntPtr hProv, uint Algid, IntPtr hKey, uint dwFlags, ref IntPtr hHash);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptHashData(IntPtr hHash, [In, Out] byte[] pbData, uint dwDataLen, uint dwSize);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDestroyHash(IntPtr hHash);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDeriveKey(IntPtr hProv, uint Algid, IntPtr hHash, uint dwFlags, ref IntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptEncrypt(IntPtr hKey, IntPtr hHash, int Final, uint dwFlags, [In, Out] byte[] pbData, ref uint pdwDataLen, uint dwBufLen);
}
I have tried different combinations, options and encryption providers, and there is no problem to create similar method using System.Security.Cryptography, but the problem is that I need a method that will replace a code.
That means that with the same data passed for encryption I must get the same result. And here is a problem. My knowledge of encryption are definitely is not so deep to take into account all nuances of this method.
Can you help me with this issue? I don't mean to give me a link to encryption tutorial, but to tell me what methods with which options I should to use.
[2017-03-28 11:27GMT] Additional information:
I really do not think that it will helps, but there is one of my experimental code that I finish with:
public static List<byte> Encrypt(byte[] toEncrypt)
{
var databytes = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
var hashmd5 = new MD5CryptoServiceProvider();
var keyArray = hashmd5.ComputeHash(databytes);
hashmd5.Clear();
var pdb = new PasswordDeriveBytes(keyArray, new byte[0]);
var hashKey = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);
var tdes = new TripleDESCryptoServiceProvider();
tdes.Key = hashKey;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
var cTransform = tdes.CreateEncryptor();
var resultArray = cTransform.TransformFinalBlock(toEncrypt, 0, toEncrypt.Length);
tdes.Clear();
return resultArray.ToList();
}
There was many other variations but no any that give me correct result:
Source data:
private byte[] dataToEncrypt = {224,111,176,138,238,238,238,239,115,109,201,144,89,58,161,0,0,0,0,0,0,0,0};
Original function reurns:
private byte[] originalResult = {31,173,65,161,199,249,73,200,210,74,156,21,36,160,94,137,71,205,15,206,99,105,40,83};
Sample function returns:
private byte[] sampleResult = {211,29,187,125,82,9,240,177,199,133,135,7,132,166,166,164,189,36,126,186,104,79,53,159};

I lost at least one hour because I thought your code was using RSA (I was mislead by the PROV_RSA_FULL)... In truth it is only doing 3DES encryption...
var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
byte[] key;
using (PasswordDeriveBytes pdb = new PasswordDeriveBytes(keyData, null))
{
key = pdb.CryptDeriveKey("TripleDES", "MD5", 0, new byte[8]);
//Debug.WriteLine(BitConverter.ToString(key));
}
using (var prov = new TripleDESCryptoServiceProvider())
{
using (var encryptor = prov.CreateEncryptor(key, new byte[8]))
{
byte[] encrypted = encryptor.TransformFinalBlock(bytes2, 0, bytes2.Length);
//Debug.WriteLine(BitConverter.ToString(encrypted));
}
}
Just out of curiousity I'll post a modified version of the original C# code... I wanted to debug a little the generated key, but it is complex to export it in CryptoAPI, and then the original code wasn't like I would have written it :-)
private static IntPtr GenerateKey(IntPtr hProv, byte[] keyData)
{
var hHash = IntPtr.Zero;
try
{
bool check = Win32.CryptCreateHash(hProv, Win32.CALG_MD5, IntPtr.Zero, 0, out hHash);
if (!check)
{
throw new Win32Exception();
}
check = Win32.CryptHashData(hHash, keyData, keyData.Length, 0);
if (!check)
{
throw new Win32Exception();
}
IntPtr hKey;
check = Win32.CryptDeriveKey(hProv, Win32.CALG_3DES, hHash, Win32.CRYPT_EXPORTABLE, out hKey);
if (!check)
{
throw new Win32Exception();
}
return hKey;
}
finally
{
if (hHash != IntPtr.Zero)
{
Win32.CryptDestroyHash(hHash);
}
}
}
public static byte[] Encrypt(byte[] plainText)
{
var keyData = Encoding.ASCII.GetBytes("{2B9B4443-74CE-42A8-8803-076B136B5967}");
IntPtr hCryptProv = IntPtr.Zero;
IntPtr hKey = IntPtr.Zero;
try
{
bool check = Win32.CryptAcquireContext(out hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
if (!check)
{
check = Win32.CryptAcquireContext(out hCryptProv, null, null, Win32.PROV_RSA_FULL, Win32.CRYPT_NEWKEYSET | Win32.CRYPT_VERIFYCONTEXT | Win32.CRYPT_MACHINE_KEYSET);
if (!check)
{
throw new Win32Exception();
}
}
hKey = GenerateKey(hCryptProv, keyData);
//byte[] key = ExportSymmetricKey(hCryptProv, hKey);
//Debug.WriteLine(BitConverter.ToString(key));
var size = plainText.Length;
check = Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, null, ref size, 0);
if (!check)
{
throw new Win32Exception();
}
var cypherText = new byte[size];
Array.Copy(plainText, cypherText, plainText.Length);
size = plainText.Length;
check = Win32.CryptEncrypt(hKey, IntPtr.Zero, 1, 0, cypherText, ref size, cypherText.Length);
if (!check)
{
throw new Win32Exception();
}
return cypherText;
}
finally
{
if (hKey != IntPtr.Zero)
{
Win32.CryptDestroyKey(hKey);
}
if (hCryptProv != IntPtr.Zero)
{
Win32.CryptReleaseContext(hCryptProv, 0);
}
}
}
// Based on https://books.google.it/books?id=aL3P3eJdiREC&pg=PT271&lpg=PT271&dq=PROV_RSA_FULL+CryptEncrypt&source=bl&ots=STsuConTHr&sig=W-BWwch8aZ-RqFb8N67rMHTrqYc&hl=it&sa=X&ved=0ahUKEwit2qKnlfvSAhWCtRQKHbL9BbQQ6AEIQzAF#v=onepage&q=PROV_RSA_FULL%20CryptEncrypt&f=false
// Page 248
private static byte[] ExportSymmetricKey(IntPtr hProv, IntPtr hKey)
{
IntPtr hExpKey = IntPtr.Zero;
try
{
bool check = Win32.CryptGenKey(hProv, 1 /* AT_KEYEXCHANGE */, 1024 << 16, out hExpKey);
if (!check)
{
throw new Win32Exception();
}
int size = 0;
check = Win32.CryptExportKey(hKey, hExpKey, 1 /* SIMPLEBLOB */, 0, null, ref size);
if (!check)
{
throw new Win32Exception();
}
var bytes = new byte[size];
check = Win32.CryptExportKey(hKey, hExpKey, 1 /* SIMPLEBLOB */, 0, bytes, ref size);
if (!check)
{
throw new Win32Exception();
}
// The next lines could be optimized by using a CryptDecrypt
// that accepts a IntPtr and adding directly 12 to the ref bytes
// instead of copying around the byte array
// 12 == sizeof(BLOBHEADER) + sizeof(ALG_ID)
var bytes2 = new byte[size - 12];
Array.Copy(bytes, 12, bytes2, 0, bytes2.Length);
bytes = bytes2;
bytes2 = null;
check = Win32.CryptDecrypt(hExpKey, IntPtr.Zero, true, 0, bytes, ref size);
if (!check)
{
throw new Win32Exception();
}
Array.Resize(ref bytes, size);
return bytes;
}
finally
{
if (hExpKey != IntPtr.Zero)
{
Win32.CryptDestroyKey(hExpKey);
}
}
}
/// <summary>
/// WinAPI Imports
/// </summary>
internal class Win32
{
public const uint PROV_RSA_FULL = 1;
public const uint NTE_BAD_KEYSET = 0x80090016;
public const uint CRYPT_NEWKEYSET = 0x00000008;
public const uint CRYPT_VERIFYCONTEXT = 0xF0000000;
public const uint CRYPT_MACHINE_KEYSET = 0x00000020;
public const uint CRYPT_EXPORTABLE = 1;
public const uint ALG_CLASS_HASH = (4 << 13);
public const uint ALG_SID_MD5 = 3;
public const uint CALG_MD5 = (ALG_CLASS_HASH | ALG_SID_MD5);
public const uint ALG_CLASS_DATA_ENCRYPT = (3 << 13);
public const uint ALG_TYPE_BLOCK = (3 << 9);
public const uint ALG_SID_3DES = 3;
public const uint CALG_3DES = (ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptAcquireContext(out IntPtr hProv, string pszContainer, string pszProvider, uint dwProvType, uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptReleaseContext(IntPtr hProv, uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDestroyKey(IntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptCreateHash(IntPtr hProv, uint Algid, IntPtr hKey, uint dwFlags, out IntPtr hHash);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptHashData(IntPtr hHash, [In, Out] byte[] pbData, int dwDataLen, uint dwSize);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDestroyHash(IntPtr hHash);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDeriveKey(IntPtr hProv, uint Algid, IntPtr hHash, uint dwFlags, out IntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptGenKey(IntPtr hProv, uint Algid, uint dwFlags, out IntPtr hKey);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptEncrypt(IntPtr hKey, IntPtr hHash, int final, uint dwFlags, [In, Out] byte[] pbData, ref int pdwDataLen, int dwBufLen);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptExportKey(IntPtr hKey, IntPtr hExpKey, uint dwBlobType, uint dwFlags, [Out] byte[] pbData, ref int pdwDataLen);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptGetKeyParam(IntPtr hKey, uint dwParam, [Out] byte[] pbData, ref int pdwDataLen, uint dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool CryptDecrypt(IntPtr hKey, IntPtr hHash, bool final, uint dwFlags, [In, Out] byte[] pbData, ref int pdwDataLen);
}

Related

Standard output from CreateProcessAsUser Always Empty in C#

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!

Reading from Protected Process Memory

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;
}
}

Problems generating a self-signed 1024-bit X509Certificate2 using the RSA AES provider

I am trying to generate an X509Certificate2 object using the Microsoft AES Cryptographic Provider:
CALG_AES_256 (0x00006610) 256 bit AES. This algorithm is supported by
the Microsoft AES Cryptographic Provider.
My problem is that my call to CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey) fails with the following error:
An unhandled exception of type
'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
Additional information: Invalid flags specified. (Exception from
HRESULT: 0x80090009)
The flags I am using are (1024 << 16) | 1). Unless I am mistaken, should this not produce a 1024-bit exportable key according to the documentation on MSDN? What is the problem with my approach here?
My code is as follows:
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
namespace WebSockets
{
public struct SystemTime
{
public short Year;
public short Month;
public short DayOfWeek;
public short Day;
public short Hour;
public short Minute;
public short Second;
public short Milliseconds;
}
public static class MarshalHelper
{
public static void CheckReturnValue(bool nativeCallSucceeded)
{
if (!nativeCallSucceeded)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public static class DateTimeExtensions
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FileTimeToSystemTime(ref long fileTime, out SystemTime systemTime);
public static SystemTime ToSystemTime(this DateTime dateTime)
{
long fileTime = dateTime.ToFileTime();
SystemTime systemTime;
MarshalHelper.CheckReturnValue(FileTimeToSystemTime(ref fileTime, out systemTime));
return systemTime;
}
}
class X509Certificate2Helper
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CryptAcquireContextW(out IntPtr providerContext, string container, string provider, uint providerType, uint flags);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptReleaseContext(IntPtr providerContext, int flags);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptGenKey(IntPtr providerContext, int algorithmId, int flags, out IntPtr cryptKeyHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptDestroyKey(IntPtr cryptKeyHandle);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertStrToNameW(int certificateEncodingType, IntPtr x500, int strType, IntPtr reserved, byte[] encoded, ref int encodedLength, out IntPtr errorString);
[DllImport("crypt32.dll", SetLastError = true)]
static extern IntPtr CertCreateSelfSignCertificate(IntPtr providerHandle, ref CryptoApiBlob subjectIssuerBlob, int flags, ref CryptKeyProviderInformation keyProviderInformation, IntPtr signatureAlgorithm, ref SystemTime startTime, ref SystemTime endTime, IntPtr extensions);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertFreeCertificateContext(IntPtr certificateContext);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertSetCertificateContextProperty(IntPtr certificateContext, int propertyId, int flags, ref CryptKeyProviderInformation data);
public static X509Certificate2 GenerateSelfSignedCertificate(String name = "", DateTime? startTime = null, DateTime? endTime = null)
{
if (startTime == null || (DateTime)startTime < DateTime.FromFileTimeUtc(0))
startTime = DateTime.FromFileTimeUtc(0);
var startSystemTime = ((DateTime)startTime).ToSystemTime();
if (endTime == null)
endTime = DateTime.MaxValue;
var endSystemTime = ((DateTime)endTime).ToSystemTime();
string containerName = Guid.NewGuid().ToString();
GCHandle dataHandle = new GCHandle();
IntPtr providerContext = IntPtr.Zero;
IntPtr cryptKey = IntPtr.Zero;
IntPtr certificateContext = IntPtr.Zero;
IntPtr algorithmPointer = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
MarshalHelper.CheckReturnValue(CryptAcquireContextW(out providerContext, containerName, null, 0x18, 0x8));
MarshalHelper.CheckReturnValue(CryptGenKey(providerContext, 0x6610, 0x4000001, out cryptKey));
IntPtr errorStringPtr;
int nameDataLength = 0;
byte[] nameData;
dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, null, ref nameDataLength, out errorStringPtr))
{
string error = Marshal.PtrToStringUni(errorStringPtr);
throw new ArgumentException(error);
}
nameData = new byte[nameDataLength];
if (!CertStrToNameW(0x10001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr))
{
string error = Marshal.PtrToStringUni(errorStringPtr);
throw new ArgumentException(error);
}
dataHandle.Free();
dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
CryptoApiBlob nameBlob = new CryptoApiBlob { cbData = (uint)nameData.Length, pbData = dataHandle.AddrOfPinnedObject() };
dataHandle.Free();
CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 };
CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "1.2.840.113549.1.1.13", Parameters = new CryptoApiBlob() };
algorithmPointer = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm));
Marshal.StructureToPtr(algorithm, algorithmPointer, false);
certificateContext = CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref keyProvider, algorithmPointer, ref startSystemTime, ref endSystemTime, IntPtr.Zero);
MarshalHelper.CheckReturnValue(certificateContext != IntPtr.Zero);
return new X509Certificate2(certificateContext);
}
finally
{
if (dataHandle.IsAllocated)
dataHandle.Free();
if (certificateContext != IntPtr.Zero)
CertFreeCertificateContext(certificateContext);
if (cryptKey != IntPtr.Zero)
CryptDestroyKey(cryptKey);
if (providerContext != IntPtr.Zero)
CryptReleaseContext(providerContext, 0);
if (algorithmPointer != IntPtr.Zero)
{
Marshal.DestroyStructure(algorithmPointer, typeof(CryptAlgorithmIdentifier));
Marshal.FreeHGlobal(algorithmPointer);
}
}
}
struct CryptoApiBlob
{
public uint cbData;
public IntPtr pbData;
}
struct CryptAlgorithmIdentifier {
[MarshalAs(UnmanagedType.LPStr)]
public String pszObjId;
public CryptoApiBlob Parameters;
}
struct CryptKeyProviderInformation
{
[MarshalAs(UnmanagedType.LPWStr)]
public String pwszContainerName;
[MarshalAs(UnmanagedType.LPWStr)]
public String pwszProvName;
public uint dwProvType;
public uint dwFlags;
public uint cProvParam;
public IntPtr rgProvParam;
public uint dwKeySpec;
}
}
}
To call it, you may use: X509Certificate2Helper.GenerateSelfSignedCertificate("CN = Example");.
Update:
If I use 0x1 for the flags:
CryptAcquireContextW(out providerContext, containerName, "Microsoft Enhanced RSA and AES Cryptographic Provider", 0x18, 0x8);
CryptGenKey(providerContext, 0x6610, 0x1, out cryptKey);
It gets past CryptGenKey, but then fails on CertCreateSelfSignCertificate with:
An unhandled exception of type
'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
Additional information: Key does not exist. (Exception from HRESULT:
0x8009000D)
Must this key-set be passed in differently? What is wrong with the way I have created it above?
The problem is with CryptGenKey function call. In the Algid parameter, you should pass either 0x1 (for RSA key exchange) or 0x2 (RSA digital signature). You don't need other values. And key length value should be 0x4000001 (with exportable key). Also, I noticed that you pass incorrect provider type when you instantiate CryptKeyProviderInformation object. Replace this line:
CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation {
pwszContainerName = containerName,
dwProvType = 1,
dwKeySpec = 1
};
with this line:
CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation {
pwszContainerName = containerName,
dwProvType = 0x18,
dwKeySpec = 1
};
Use this formula
(keySize * 65536) | 1;
For a 2048bit key it is 0x08000001. According to documentation of CryptGenKey method you can use RSA1024BIT_KEY to generate 1024bit key. I've tried looking for the define and found this (although it was on Adobe site :) )
#define RSA1024BIT_KEY 0x04000000

Generating a self-signed X509Certificate2 certificate with its private key

I'm not the most familiar with the unmanaged cryptography library in the Windows API, but alas I am trying to generate a self-signed X509Certificate2 certificate.
Here is the complete code:
using System;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
namespace Example
{
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
public short Year;
public short Month;
public short DayOfWeek;
public short Day;
public short Hour;
public short Minute;
public short Second;
public short Milliseconds;
}
public static class MarshalHelper
{
public static void ErrorCheck(bool nativeCallSucceeded)
{
if (!nativeCallSucceeded)
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
}
public static class DateTimeExtensions
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FileTimeToSystemTime(ref long fileTime, out SystemTime systemTime);
public static SystemTime ToSystemTime(this DateTime dateTime)
{
long fileTime = dateTime.ToFileTime();
SystemTime systemTime;
MarshalHelper.ErrorCheck(FileTimeToSystemTime(ref fileTime, out systemTime));
return systemTime;
}
}
class X509Certificate2Helper
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool CryptAcquireContextW(out IntPtr providerContext, string container, string provider, int providerType, int flags);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptReleaseContext(IntPtr providerContext, int flags);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptGenKey(IntPtr providerContext, int algorithmId, int flags, out IntPtr cryptKeyHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool CryptDestroyKey(IntPtr cryptKeyHandle);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertStrToNameW(int certificateEncodingType, IntPtr x500, int strType, IntPtr reserved, byte[] encoded, ref int encodedLength, out IntPtr errorString);
[DllImport("crypt32.dll", SetLastError = true)]
static extern IntPtr CertCreateSelfSignCertificate(IntPtr providerHandle, ref CryptoApiBlob subjectIssuerBlob, int flags, ref CryptKeyProviderInformation keyProviderInformation, IntPtr signatureAlgorithm, ref SystemTime startTime, ref SystemTime endTime, IntPtr extensions);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertFreeCertificateContext(IntPtr certificateContext);
[DllImport("crypt32.dll", SetLastError = true)]
static extern bool CertSetCertificateContextProperty(IntPtr certificateContext, int propertyId, int flags, ref CryptKeyProviderInformation data);
public static X509Certificate2 GenerateSelfSignedCertificate(String name = "CN = Example", DateTime? startTime = null, DateTime? endTime = null)
{
if (name == null)
name = String.Empty;
var startSystemTime = default(SystemTime);
if (startTime == null || (DateTime)startTime < DateTime.FromFileTimeUtc(0))
startTime = DateTime.FromFileTimeUtc(0);
var startSystemTime = ((DateTime)startTime).ToSystemTime();
if (endTime == null)
endTime = DateTime.MaxValue;
var endSystemTime = ((DateTime)endTime).ToSystemTime();
string containerName = Guid.NewGuid().ToString();
GCHandle dataHandle = new GCHandle();
IntPtr providerContext = IntPtr.Zero;
IntPtr cryptKey = IntPtr.Zero;
IntPtr certificateContext = IntPtr.Zero;
IntPtr algorithmPointer = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
MarshalHelper.ErrorCheck(CryptAcquireContextW(out providerContext, containerName, null, 1, 0x8));
MarshalHelper.ErrorCheck(CryptGenKey(providerContext, 1, 0x8000001, out cryptKey));
IntPtr errorStringPtr;
int nameDataLength = 0;
byte[] nameData;
dataHandle = GCHandle.Alloc(name, GCHandleType.Pinned);
if (!CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, null, ref nameDataLength, out errorStringPtr))
{
string error = Marshal.PtrToStringUni(errorStringPtr);
throw new ArgumentException(error);
}
nameData = new byte[nameDataLength];
if (!CertStrToNameW(0x00010001, dataHandle.AddrOfPinnedObject(), 3, IntPtr.Zero, nameData, ref nameDataLength, out errorStringPtr))
{
string error = Marshal.PtrToStringUni(errorStringPtr);
throw new ArgumentException(error);
}
dataHandle.Free();
dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
CryptoApiBlob nameBlob = new CryptoApiBlob { cbData = nameData.Length, pbData = dataHandle.AddrOfPinnedObject() };
dataHandle.Free();
CryptKeyProviderInformation keyProvider = new CryptKeyProviderInformation { pwszContainerName = containerName, dwProvType = 1, dwKeySpec = 1 };
CryptAlgorithmIdentifier algorithm = new CryptAlgorithmIdentifier { pszObjId = "1.2.840.113549.1.1.13", Parameters = new CryptoApiBlob() };
algorithmPointer = Marshal.AllocHGlobal(Marshal.SizeOf(algorithm));
Marshal.StructureToPtr(algorithm, algorithmPointer, false);
certificateContext = CertCreateSelfSignCertificate(providerContext, ref nameBlob, 0, ref keyProvider, algorithmPointer, ref startSystemTime, ref endSystemTime, IntPtr.Zero);
MarshalHelper.ErrorCheck(certificateContext != IntPtr.Zero);
return new X509Certificate2(certificateContext);
}
finally
{
if (dataHandle.IsAllocated)
dataHandle.Free();
if (certificateContext != IntPtr.Zero)
CertFreeCertificateContext(certificateContext);
if (cryptKey != IntPtr.Zero)
CryptDestroyKey(cryptKey);
if (providerContext != IntPtr.Zero)
CryptReleaseContext(providerContext, 0);
if (algorithmPointer != IntPtr.Zero)
{
Marshal.DestroyStructure(algorithmPointer, typeof(CryptAlgorithmIdentifier));
Marshal.FreeHGlobal(algorithmPointer);
}
}
}
struct CryptoApiBlob
{
public Int32 cbData;
public IntPtr pbData;
}
struct CryptAlgorithmIdentifier {
public String pszObjId;
public CryptoApiBlob Parameters;
}
struct CryptKeyProviderInformation
{
public String pwszContainerName;
public String pwszProvName;
public Int32 dwProvType;
public Int32 dwFlags;
public Int32 cProvParam;
public IntPtr rgProvParam;
public Int32 dwKeySpec;
}
}
}
Here is how you can generate a new X509Certificate2 using it:
var certificate = X509Certificate2Helper.GenerateSelfSignedCertificate();
However, you can see that trying to get the private key through certificate.PrivateKey throws Keyset does not exist. I've tried to consult the documentation but I couldn't figure out why the certificate context doesn't have its private key set when its loaded as an X509Certificate2. Does anyone have any ideas? Are there problems with the implementation that cause the key to not be set? I mean, I'm a little bit confused here because I would expect a self-signed certificate to always carry its private key since its signed itself using it, or is this not the case?
The problem is with CryptKeyProviderInformation structure signature. It is missing CharSet (with either, Auto or Unicode) attribute, because container and provider names are expected to be unicode (after marshalling). Update structure definition as follows:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct CryptKeyProviderInformation
{
public String pwszContainerName;
public String pwszProvName;
public Int32 dwProvType;
public Int32 dwFlags;
public Int32 cProvParam;
public IntPtr rgProvParam;
public Int32 dwKeySpec;
}
and the key should be accessbile after that.

Read other process current directory in C#

I am trying to get current working directory of selected process. I am sure that it is possible, because tools like Process Explorer can show this information. I found out that this information is stored in PEB.
Moreover I found Oleksiy blog post: http://eazfuscator.blogspot.com/2011/06/reading-environment-variables-from.html, but all my modifications are totally unless.
So my question is how can I read this information in C#? If Oleksiy code is a good way, how should I modify his code to get this information?
As other pointed out, this is completely undocumented, but here is a piece of code that does it (as of today, it seems to work). Of course, it needs to run as admin. Note it can work for any process bitness, 32-bit or 64-bit (some processess may report an access denied, even running as admin), with any compilation target (x86 or x64). It also is capable of reading the command line. Use at your own risk!
// All offset values below have been tested on Windows 7 & 8 only
// but you can use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth, depending on the OS version
public static class ProcessUtilities
{
public static string GetCurrentDirectory(int processId)
{
return GetProcessParametersString(processId, Environment.Is64BitOperatingSystem ? 0x38 : 0x24);
}
public static string GetCurrentDirectory(this Process process)
{
if (process == null)
throw new ArgumentNullException("process");
return GetCurrentDirectory(process.Id);
}
public static string GetCommandLine(int processId)
{
return GetProcessParametersString(processId, Environment.Is64BitOperatingSystem ? 0x70 : 0x40);
}
public static string GetCommandLine(this Process process)
{
if (process == null)
throw new ArgumentNullException("process");
return GetCommandLine(process.Id);
}
private static string GetProcessParametersString(int processId, int offset)
{
IntPtr handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
int processParametersOffset = Environment.Is64BitOperatingSystem ? 0x20 : 0x10;
try
{
if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) // are we running in WOW?
{
PROCESS_BASIC_INFORMATION_WOW64 pbi = new PROCESS_BASIC_INFORMATION_WOW64();
int hr = NtWow64QueryInformationProcess64(handle, 0, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
long pp = 0;
hr = NtWow64ReadVirtualMemory64(handle, pbi.PebBaseAddress + processParametersOffset, ref pp, Marshal.SizeOf(pp), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
UNICODE_STRING_WOW64 us = new UNICODE_STRING_WOW64();
hr = NtWow64ReadVirtualMemory64(handle, pp + offset, ref us, Marshal.SizeOf(us), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
if ((us.Buffer == 0) || (us.Length == 0))
return null;
string s = new string('\0', us.Length / 2);
hr = NtWow64ReadVirtualMemory64(handle, us.Buffer, s, us.Length, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
return s;
}
else // we are running with the same bitness as the OS, 32 or 64
{
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
int hr = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
IntPtr pp = new IntPtr();
if (!ReadProcessMemory(handle, pbi.PebBaseAddress + processParametersOffset, ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
UNICODE_STRING us = new UNICODE_STRING();
if (!ReadProcessMemory(handle, pp + offset, ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
if ((us.Buffer == IntPtr.Zero) || (us.Length == 0))
return null;
string s = new string('\0', us.Length / 2);
if (!ReadProcessMemory(handle, us.Buffer, s, new IntPtr(us.Length), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
return s;
}
}
finally
{
CloseHandle(handle);
}
}
private const int PROCESS_QUERY_INFORMATION = 0x400;
private const int PROCESS_VM_READ = 0x10;
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
public IntPtr Reserved2_0;
public IntPtr Reserved2_1;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
}
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING
{
public short Length;
public short MaximumLength;
public IntPtr Buffer;
}
// for 32-bit process in a 64-bit OS only
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_BASIC_INFORMATION_WOW64
{
public long Reserved1;
public long PebBaseAddress;
public long Reserved2_0;
public long Reserved2_1;
public long UniqueProcessId;
public long Reserved3;
}
// for 32-bit process in a 64-bit OS only
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING_WOW64
{
public short Length;
public short MaximumLength;
public long Buffer;
}
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
// for 32-bit process in a 64-bit OS only
[DllImport("ntdll.dll")]
private static extern int NtWow64QueryInformationProcess64(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION_WOW64 ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref long lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
}
"Simon Mourier" source are work fine in OS,Executor,Target is 32bit. But others are not.
So I Add source to handle all combination of 32, 64bit.
There are 5 possible combination of 32, 64bit process.
First, os,executor,target are 32bit. Second, os is 64bit, executor, target are combination of 32,64bit process.
This code is work fine in My notebook Win7 64Bit OS, 32,64bit Process & target 32,64bit Process, WinXp 32bit , exeutor,target are 32bit. But, Use at your own risk!
// ref: http://www.microsoft.com/whdc/system/Sysinternals/MoreThan64proc.mspx
public enum PROCESSINFOCLASS : int
{
ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
ProcessWow64Information = 26, // q: ULONG_PTR
}
[Flags]
public enum PEB_OFFSET
{
CurrentDirectory,
//DllPath,
//ImagePathName,
CommandLine,
//WindowTitle,
//DesktopInfo,
//ShellInfo,
//RuntimeData,
//TypeMask = 0xffff,
//Wow64 = 0x10000,
};
public class Is64BitChecker
{
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWow64Process(
[In] IntPtr hProcess,
[Out] out bool wow64Process
);
public static bool GetProcessIsWow64(IntPtr hProcess)
{
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
Environment.OSVersion.Version.Major >= 6)
{
bool retVal;
if (!IsWow64Process(hProcess, out retVal))
{
return false;
}
return retVal;
}
else
{
return false;
}
}
public static bool InternalCheckIsWow64()
{
if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
Environment.OSVersion.Version.Major >= 6)
{
using (Process p = Process.GetCurrentProcess())
{
bool retVal;
if (!IsWow64Process(p.Handle, out retVal))
{
return false;
}
return retVal;
}
}
else
{
return false;
}
}
}
// All offset values below have been tested on Windows 7 & 8 only
// but you can use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth, depending on the OS version
public static class ProcessUtilities
{
public static readonly bool Is64BitProcess = IntPtr.Size > 4;
public static readonly bool Is64BitOperatingSystem = Is64BitProcess || Is64BitChecker.InternalCheckIsWow64();
public static string GetCurrentDirectory(int processId)
{
return GetProcessParametersString(processId, PEB_OFFSET.CurrentDirectory);
}
public static string GetCurrentDirectory(this Process process)
{
if (process == null)
throw new ArgumentNullException("process");
return GetCurrentDirectory(process.Id);
}
#region GetCommandLine
//public static string GetCommandLine(int processId)
//{
// return null;// GetProcessParametersString(processId, Is64BitOperatingSystem ? 0x70 : 0x40);
//}
//public static string GetCommandLine(this Process process)
//{
// if (process == null)
// throw new ArgumentNullException("process");
// return GetCommandLine(process.Id);
//}
#endregion
private static string GetProcessParametersString(int processId, PEB_OFFSET Offset)
{
IntPtr handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
bool IsWow64Process = Is64BitChecker.InternalCheckIsWow64();
bool IsTargetWow64Process = Is64BitChecker.GetProcessIsWow64(handle);
bool IsTarget64BitProcess = Is64BitOperatingSystem && !IsTargetWow64Process;
long offset = 0;
long processParametersOffset = IsTarget64BitProcess ? 0x20 : 0x10;
switch (Offset)
{
case PEB_OFFSET.CurrentDirectory:
offset = IsTarget64BitProcess ? 0x38 : 0x24;
break;
case PEB_OFFSET.CommandLine:
default:
return null;
}
try
{
long pebAddress = 0;
if (IsTargetWow64Process) // OS : 64Bit, Cur : 32 or 64, Tar: 32bit
{
IntPtr peb32 = new IntPtr();
int hr = NtQueryInformationProcess(handle, (int)PROCESSINFOCLASS.ProcessWow64Information, ref peb32, IntPtr.Size, IntPtr.Zero);
if (hr != 0) throw new Win32Exception(hr);
pebAddress = peb32.ToInt64();
IntPtr pp = new IntPtr();
if (!ReadProcessMemory(handle, new IntPtr(pebAddress + processParametersOffset), ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
UNICODE_STRING_32 us = new UNICODE_STRING_32();
if (!ReadProcessMemory(handle, new IntPtr(pp.ToInt64() + offset), ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
if ((us.Buffer == 0) || (us.Length == 0))
return null;
string s = new string('\0', us.Length / 2);
if (!ReadProcessMemory(handle, new IntPtr(us.Buffer), s, new IntPtr(us.Length), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
return s;
}
else if (IsWow64Process)//Os : 64Bit, Cur 32, Tar 64
{
PROCESS_BASIC_INFORMATION_WOW64 pbi = new PROCESS_BASIC_INFORMATION_WOW64();
int hr = NtWow64QueryInformationProcess64(handle, (int)PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
if (hr != 0) throw new Win32Exception(hr);
pebAddress = pbi.PebBaseAddress;
long pp = 0;
hr = NtWow64ReadVirtualMemory64(handle, pebAddress + processParametersOffset, ref pp, Marshal.SizeOf(pp), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
UNICODE_STRING_WOW64 us = new UNICODE_STRING_WOW64();
hr = NtWow64ReadVirtualMemory64(handle, pp + offset, ref us, Marshal.SizeOf(us), IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
if ((us.Buffer == 0) || (us.Length == 0))
return null;
string s = new string('\0', us.Length / 2);
hr = NtWow64ReadVirtualMemory64(handle, us.Buffer, s, us.Length, IntPtr.Zero);
if (hr != 0)
throw new Win32Exception(hr);
return s;
}
else// Os,Cur,Tar : 64 or 32
{
PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
int hr = NtQueryInformationProcess(handle, (int)PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
if (hr != 0) throw new Win32Exception(hr);
pebAddress = pbi.PebBaseAddress.ToInt64();
IntPtr pp = new IntPtr();
if (!ReadProcessMemory(handle, new IntPtr(pebAddress + processParametersOffset), ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
UNICODE_STRING us = new UNICODE_STRING();
if (!ReadProcessMemory(handle, new IntPtr((long)pp + offset), ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
if ((us.Buffer == IntPtr.Zero) || (us.Length == 0))
return null;
string s = new string('\0', us.Length / 2);
if (!ReadProcessMemory(handle, us.Buffer, s, new IntPtr(us.Length), IntPtr.Zero))
throw new Win32Exception(Marshal.GetLastWin32Error());
return s;
}
}
finally
{
CloseHandle(handle);
}
}
private const int PROCESS_QUERY_INFORMATION = 0x400;
private const int PROCESS_VM_READ = 0x10;
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebBaseAddress;
public IntPtr Reserved2_0;
public IntPtr Reserved2_1;
public IntPtr UniqueProcessId;
public IntPtr Reserved3;
}
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING
{
public short Length;
public short MaximumLength;
public IntPtr Buffer;
}
// for 32-bit process in a 64-bit OS only
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_BASIC_INFORMATION_WOW64
{
public long Reserved1;
public long PebBaseAddress;
public long Reserved2_0;
public long Reserved2_1;
public long UniqueProcessId;
public long Reserved3;
}
// for 32-bit process
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING_WOW64
{
public short Length;
public short MaximumLength;
public long Buffer;
}
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING_32
{
public short Length;
public short MaximumLength;
public int Buffer;
}
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);
//ProcessWow64Information, // q: ULONG_PTR
[DllImport("ntdll.dll")]
private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref IntPtr ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_32 lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
//[DllImport("kernel32.dll", SetLastError = true)]
//private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hObject);
// for 32-bit process in a 64-bit OS only
[DllImport("ntdll.dll")]
private static extern int NtWow64QueryInformationProcess64(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION_WOW64 ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref long lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("ntdll.dll")]
private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
}

Categories

Resources