Skip to content

Instantly share code, notes, and snippets.

@rasta-mouse
Created December 20, 2022 22:19
Show Gist options
  • Save rasta-mouse/2f6316083dd2f38bb91f160cca2088df to your computer and use it in GitHub Desktop.
Save rasta-mouse/2f6316083dd2f38bb91f160cca2088df to your computer and use it in GitHub Desktop.
Attempt at NtCreateUserProcess in C# (not working)
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace CreateProcess;
internal static class Program
{
public static void Main(string[] args)
{
var imagePath = new UNICODE_STRING();
RtlInitUnicodeString(ref imagePath, @"\\??\\C:\\Windows\\System32\\mmc.exe");
var processParams = IntPtr.Zero;
var status = RtlCreateProcessParametersEx(
ref processParams,
ref imagePath,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
IntPtr.Zero,
0x01);
if (status != 0)
{
Console.WriteLine("RtlCreateProcessParametersEx failed");
return;
}
var ci = new PS_CREATE_INFO();
ci.Size = (UIntPtr) Marshal.SizeOf(ci);
ci.State = PS_CREATE_STATE.PsCreateInitialState;
var attributeList = new PS_ATTRIBUTE_LIST();
attributeList.TotalLength = (UIntPtr) Marshal.SizeOf(attributeList);
attributeList.Attributes = new PS_ATTRIBUTE[2];
attributeList.Attributes[0].Attribute = 0x20005;
attributeList.Attributes[0].Size = imagePath.Length;
attributeList.Attributes[0].Value = imagePath.Buffer;
using var self = Process.GetCurrentProcess();
attributeList.Attributes[1].Attribute = 0x60000;
attributeList.Attributes[1].Size = (ushort) IntPtr.Size;
attributeList.Attributes[1].Value = self.Handle;
var hProcess = IntPtr.Zero;
var hThread = IntPtr.Zero;
status = NtCreateUserProcess(
ref hProcess,
ref hThread,
2097151,
2097151,
IntPtr.Zero,
IntPtr.Zero,
0,
0,
processParams,
ref ci,
ref attributeList);
if (status != 0)
Console.WriteLine("NtCreateUserProcess failed");
}
[DllImport("ntdll.dll")]
private static extern void RtlInitUnicodeString(
ref UNICODE_STRING destinationString,
[MarshalAs(UnmanagedType.LPWStr)] string sourceString);
[DllImport("ntdll.dll")]
private static extern uint RtlCreateProcessParametersEx(
ref IntPtr processParameters,
ref UNICODE_STRING imagePathName,
IntPtr dllPath,
IntPtr currentDirectory,
IntPtr commandLine,
IntPtr environment,
IntPtr windowTitle,
IntPtr desktopInfo,
IntPtr shellInfo,
IntPtr runtimeData,
uint flags);
[DllImport("ntdll.dll")]
private static extern uint NtCreateUserProcess(
ref IntPtr processHandle,
ref IntPtr threadHandle,
long processDesiredAccess,
long threadDesiredAccess,
IntPtr processObjectAttributes,
IntPtr threadObjectAttributes,
uint processFlags,
uint threadFlags,
IntPtr processParameters,
ref PS_CREATE_INFO psCreateInfo,
ref PS_ATTRIBUTE_LIST psAttributeList);
[StructLayout(LayoutKind.Sequential)]
private struct PS_CREATE_INFO
{
public UIntPtr Size;
public PS_CREATE_STATE State;
}
private enum PS_CREATE_STATE
{
PsCreateInitialState = 0,
PsCreateFailOnFileOpen = 1,
PsCreateFailOnSectionCreate = 2,
PsCreateFailExeFormat = 3,
PsCreateFailMachineMismatch = 4,
PsCreateFailExeName = 5,
PsCreateSuccess = 6,
PsCreateMaximumStates = 7
};
[StructLayout(LayoutKind.Sequential)]
private struct PS_ATTRIBUTE
{
public ulong Attribute;
public ushort Size;
public IntPtr Value;
public IntPtr ReturnLength;
}
[StructLayout(LayoutKind.Sequential)]
private struct PS_ATTRIBUTE_LIST
{
public UIntPtr TotalLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public PS_ATTRIBUTE[] Attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
public IntPtr Buffer;
}
}
@Kudaes
Copy link

Kudaes commented Dec 21, 2022

Hi, i fixed a few things from your code and it's working for me. Still a dirty code, I guess it can be improved a little bit.

Using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace NtCreateUserProcess
{
    internal static class Program
    {
        public static void Main(string[] args)
        {
            var imagePath = new UNICODE_STRING();
            RtlInitUnicodeString(ref imagePath, @"\??\C:\Windows\System32\calc.exe");

            var processParams = IntPtr.Zero;
            var status = RtlCreateProcessParametersEx(
                ref processParams,
                ref imagePath,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                IntPtr.Zero,
                0x01);

            if (status != 0)
            {
                Console.WriteLine("RtlCreateProcessParametersEx failed");
                return;
            }

            var ci = new PS_CREATE_INFO();
            ci.Size = (UIntPtr)88; // sizeof(PS_CREATE_INFO)
            ci.State = PS_CREATE_STATE.PsCreateInitialState;
            ci.unused = new byte[76];

            var attribute = new PS_ATTRIBUTE();
            var attributeList = new PS_ATTRIBUTE_LIST();
            attributeList.TotalLength = (UIntPtr)40; // this is sizeof(PS_ATTRIBUTE_LIST) - sizeof(PS_ATTRIBUTE) 
            attributeList.Attributes = new PS_ATTRIBUTE[2];

            attributeList.Attributes[0].Attribute = 0x20005;
            attributeList.Attributes[0].Size = imagePath.Length;
            attributeList.Attributes[0].Value = imagePath.Buffer;

            var hProcess = IntPtr.Zero;
            var hThread = IntPtr.Zero;

            status = NtCreateUserProcess(
                ref hProcess,
                ref hThread,
                2097151,
                2097151,
                IntPtr.Zero,
                IntPtr.Zero,
                0,
                0,
                processParams,
                ref ci,
                ref attributeList);

            if (status != 0)
            {
                int toBase = 16;

                string hex = Convert.ToString(status, toBase);
                Console.WriteLine(hex);
            }
        }

        [DllImport("ntdll.dll")]
        private static extern void RtlInitUnicodeString(
            ref UNICODE_STRING destinationString,
            [MarshalAs(UnmanagedType.LPWStr)] string sourceString);

        [DllImport("ntdll.dll")]
        private static extern uint RtlCreateProcessParametersEx(
            ref IntPtr processParameters,
            ref UNICODE_STRING imagePathName,
            IntPtr dllPath,
            IntPtr currentDirectory,
            IntPtr commandLine,
            IntPtr environment,
            IntPtr windowTitle,
            IntPtr desktopInfo,
            IntPtr shellInfo,
            IntPtr runtimeData,
            uint flags);

        [DllImport("ntdll.dll")]
        private static extern uint NtCreateUserProcess(
            ref IntPtr processHandle,
            ref IntPtr threadHandle,
            long processDesiredAccess,
            long threadDesiredAccess,
            IntPtr processObjectAttributes,
            IntPtr threadObjectAttributes,
            uint processFlags,
            uint threadFlags,
            IntPtr processParameters,
            ref PS_CREATE_INFO psCreateInfo,
            ref PS_ATTRIBUTE_LIST psAttributeList);

        [StructLayout(LayoutKind.Sequential)]
        private struct PS_CREATE_INFO
        {
            public UIntPtr Size;
            public PS_CREATE_STATE State;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 76)]
            public byte[] unused;
        }

        private enum PS_CREATE_STATE
        {
            PsCreateInitialState = 0,
            PsCreateFailOnFileOpen = 1,
            PsCreateFailOnSectionCreate = 2,
            PsCreateFailExeFormat = 3,
            PsCreateFailMachineMismatch = 4,
            PsCreateFailExeName = 5,
            PsCreateSuccess = 6,
            PsCreateMaximumStates = 7
        };

        [StructLayout(LayoutKind.Sequential)]
        private struct PS_ATTRIBUTE
        {
            public ulong Attribute;
            public ulong Size;
            public IntPtr Value;
            public IntPtr ReturnLength;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct PS_ATTRIBUTE_LIST
        {
            public UIntPtr TotalLength;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public PS_ATTRIBUTE[] Attributes;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct UNICODE_STRING
        {
            public ushort Length;
            public ushort MaximumLength;
            public IntPtr Buffer;
        }
    }
}

@ProtoDroidBot
Copy link

@Kudaes , Thanks for the update. I was able to further simplify the code and arrived at a weird conclusion that the Marshaled pointers are not being set up correctly. Not sure how as my C# and NT Internals is a bit rusty. :(

@rasta-mouse Is the original code setting up the Marshal.SizeOf pointers correctly? Seems like these variables are the root cause of the issues:

#L35 ci.Size = (UIntPtr) Marshal.SizeOf(ci);
#L39 attributeList.TotalLength = (UIntPtr) Marshal.SizeOf(attributeList);

#L12 Thinking the image path can be single slashes, as that produces a different error code even with the marshal fixes that Kudaes mentioned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment