Ejemplo n.º 1
0
        public async Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
            var pregeneratedRoutineId = intent.Id.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intent.Id,
                MethodId  = intent.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId    = intent.ServiceId,
                Routine      = routineDescriptor,
                Caller       = intent.Caller,
                Continuation = intent.Continuation,
                Parameters   = _serializer.SerializeToString(intent.Parameters)
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (intent.Caller?.ServiceId.ServiceName ?? ""),
                EventID            = intent.Id.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var message = new CloudQueueMessage(
                JsonConvert.SerializeObject(eventEnvelope,
                                            CloudEventsSerialization.JsonSerializerSettings));

            while (true)
            {
                try
                {
                    await _transitionsQueue.AddMessageAsync(message, null, null, ct);

                    break;
                }
                catch (QueueDoesNotExistException)
                {
                    await _transitionsQueue.CreateAsync(ct);
                }
            }

            return(new ActiveRoutineInfo
            {
                RoutineId = pregeneratedRoutineId
            });
        }
Ejemplo n.º 2
0
        internal static Span <byte> AssembleRoutine32(RoutineDescriptor routineDescriptor)
        {
            var routineInstructions = new List <byte>();

            foreach (var parameter in routineDescriptor.Parameters.Select(parameter => (int)parameter).Reverse())
            {
                if (parameter <= sbyte.MaxValue)
                {
                    // push parameter

                    routineInstructions.AddRange(stackalloc byte[] { 0x6A, (byte)parameter });
Ejemplo n.º 3
0
 public void Initialize()
 {
     _routineDescriptor = GetValueOrDefault <RoutineDescriptor>();
     if (_routineDescriptor != null)
     {
         var routineRecord = _fabric.DataStore.GetRoutineRecord(_routineDescriptor.RoutineId);
         if (routineRecord != null)
         {
             _routineDescriptor.ETag = routineRecord.ETag;
         }
     }
 }
Ejemplo n.º 4
0
        public Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
#warning Need to send message first, then create routine record.

            var routineId = Interlocked.Increment(ref _dataStore.RoutineCounter);

            var routineRecord = new RoutineStateRecord
            {
                ETag         = DateTime.UtcNow.Ticks.ToString("X16"),
                Id           = routineId.ToString(),
                Completion   = new TaskCompletionSource <string>(),
                Continuation = intent.Continuation == null ? null : _serializer.SerializeToString(intent.Continuation)
            };

            lock (_dataStore.Routines)
            {
                _dataStore.Routines.Add(routineRecord.Id, routineRecord);
            }

            var transitionDescriptor = new TransitionDescriptor
            {
                Type = TransitionType.InvokeRoutine,
                ETag = routineRecord.ETag
            };

            var routineDescriptor = new RoutineDescriptor
            {
                MethodId  = intent.MethodId,
                IntentId  = intent.Id,
                RoutineId = routineRecord.Id,
                ETag      = routineRecord.ETag
            };

            var message = new Message
            {
                //["IntentId"] = _serializer.Serialize(intent.Id),
                [nameof(TransitionDescriptor)] = _serializer.SerializeToString(transitionDescriptor),
                [nameof(ServiceId)]            = _serializer.SerializeToString(intent.ServiceId),
                [nameof(RoutineDescriptor)]    = _serializer.SerializeToString(routineDescriptor),
                ["Parameters"] = _serializer.SerializeToString(intent.Parameters)
            };

            _dataStore.ScheduleMessage(message);

            var info = new ActiveRoutineInfo
            {
                RoutineId = routineRecord.Id
            };

            return(Task.FromResult(info));
        }
Ejemplo n.º 5
0
        internal static Span <byte> AssembleRoutine32(RoutineDescriptor routineDescriptor)
        {
            var routineInstructions = new List <byte>();

            for (var parameterIndex = routineDescriptor.Parameters.Count - 1; parameterIndex >= 0; parameterIndex -= 1)
            {
                var parameter = (int)routineDescriptor.Parameters[parameterIndex];

                if (parameter <= sbyte.MaxValue)
                {
                    // push parameter

                    routineInstructions.AddRange(stackalloc byte[] { 0x6A, (byte)parameter });
Ejemplo n.º 6
0
        public void OnRoutineStart(
            ServiceId serviceId,
            RoutineDescriptor routineDesc,
            object serviceInstance,
            MethodInfo routineMethod,
            IAsyncStateMachine routineStateMachine)
        {
            Context.ServiceId           = serviceId;
            Context.RoutineDescriptor   = routineDesc;
            Context.ServiceInstance     = serviceInstance;
            Context.RoutineMethod       = routineMethod;
            Context.RoutineStateMachine = routineStateMachine;

            _intrinsicFlowController.OnRoutineStart(this);
        }
Ejemplo n.º 7
0
        public Task <ActiveRoutineInfo> ScheduleRoutineAsync(
            ExecuteRoutineIntent intent, CancellationToken ct)
        {
            var pregeneratedRoutineId = intent.Id.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intent.Id,
                MethodId  = intent.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId    = intent.ServiceId,
                Routine      = routineDescriptor,
                Caller       = intent.Caller,
                Continuation = intent.Continuation,
                Parameters   = _serializer.SerializeToString(intent.Parameters)
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (intent.Caller?.ServiceId.ServiceName ?? ""),
                EventID            = intent.Id.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var fileName = intent.Id.ToString() + ".json";
            var filePath = Path.Combine(_transitionsDirectory, fileName);
            var content  = CloudEventsSerialization.Serialize(eventEnvelope);

            File.WriteAllText(filePath, content, Encoding.UTF8);

            var info = new ActiveRoutineInfo
            {
                RoutineId = pregeneratedRoutineId
            };

            return(Task.FromResult(info));
        }
Ejemplo n.º 8
0
        private void CallRoutine(RoutineDescriptor routineDescriptor)
        {
            // Write the shellcode used to perform the function call into a buffer

            Span <byte> shellcodeBuffer;

            if (Process.GetArchitecture() == Architecture.X86)
            {
                shellcodeBuffer = RoutineAssembler.AssembleRoutine32(routineDescriptor);
            }

            else
            {
                shellcodeBuffer = RoutineAssembler.AssembleRoutine64(routineDescriptor);
            }

            var shellcodeBufferAddress = Process.AllocateBuffer(shellcodeBuffer.Length, true);

            try
            {
                Process.WriteBuffer(shellcodeBufferAddress, shellcodeBuffer);

                // Create a thread to execute the shellcode

                var ntStatus = Ntdll.NtCreateThreadEx(out var threadHandle, AccessMask.SpecificRightsAll | AccessMask.StandardRightsAll, IntPtr.Zero, Process.SafeHandle, shellcodeBufferAddress, IntPtr.Zero, ThreadCreationFlags.HideFromDebugger | ThreadCreationFlags.SkipThreadAttach, IntPtr.Zero, 0, 0, IntPtr.Zero);

                using (threadHandle)
                {
                    if (ntStatus != NtStatus.Success)
                    {
                        throw new Win32Exception(Ntdll.RtlNtStatusToDosError(ntStatus));
                    }

                    if (Kernel32.WaitForSingleObject(threadHandle, int.MaxValue) == -1)
                    {
                        throw new Win32Exception();
                    }
                }
            }

            finally
            {
                Process.FreeBuffer(shellcodeBufferAddress);
            }
        }
Ejemplo n.º 9
0
        internal TStructure CallRoutine <TStructure>(IntPtr functionAddress, params dynamic[] parameters) where TStructure : unmanaged
        {
            var returnBuffer = Process.AllocateBuffer(Unsafe.SizeOf <TStructure>());

            var routineDescriptor = new RoutineDescriptor(functionAddress, parameters, returnBuffer);

            try
            {
                CallRoutine(routineDescriptor);

                return(Process.ReadStructure <TStructure>(returnBuffer));
            }

            finally
            {
                Process.FreeBuffer(returnBuffer);
            }
        }
Ejemplo n.º 10
0
        internal void ScheduleRoutineFromEvent(EventSubscriberDescriptor eventSubscriberDescriptor, RoutineEventData raisedEventData)
        {
            var intentId = _idGenerator.NewId();

            var pregeneratedRoutineId = intentId.ToString();

            var routineDescriptor = new RoutineDescriptor
            {
                IntentId  = intentId,
                MethodId  = eventSubscriberDescriptor.MethodId,
                RoutineId = pregeneratedRoutineId
            };

            var eventData = new RoutineEventData
            {
                ServiceId = eventSubscriberDescriptor.ServiceId,
                Routine   = routineDescriptor,
                Caller    = new CallerDescriptor
                {
                    ServiceId = raisedEventData.ServiceId
                },
                Parameters = raisedEventData.Parameters
            };

            var eventEnvelope = new RoutineEventEnvelope
            {
                CloudEventsVersion = CloudEventsEnvelope.Version,
                EventType          = DasyncCloudEventsTypes.InvokeRoutine.Name,
                EventTypeVersion   = DasyncCloudEventsTypes.InvokeRoutine.Version,
                Source             = "/" + (raisedEventData.ServiceId?.ServiceName ?? ""),
                EventID            = intentId.ToString(),
                EventTime          = DateTimeOffset.Now,
                ContentType        = "application/json",
                Data = CloudEventsSerialization.Serialize(eventData)
            };

            var fileName = intentId.ToString() + ".json";
            var filePath = Path.Combine(_transitionsDirectory, fileName);
            var content  = CloudEventsSerialization.Serialize(eventEnvelope);

            File.WriteAllText(filePath, content, Encoding.UTF8);
        }
Ejemplo n.º 11
0
        internal TStructure CallRoutine <TStructure>(CallingConvention callingConvention, IntPtr functionAddress, params long[] parameters) where TStructure : unmanaged
        {
            // Write the shellcode used to perform the function call into a buffer

            var returnBuffer = Process.AllocateMemory(Unsafe.SizeOf <TStructure>(), ProtectionType.ReadWrite);

            var routineDescriptor = new RoutineDescriptor(Process.GetArchitecture(), callingConvention, functionAddress, parameters, returnBuffer);

            var shellcode = Assembler.AssembleRoutine(routineDescriptor);

            var shellcodeBuffer = Process.AllocateMemory(shellcode.Length, ProtectionType.ExecuteReadWrite);

            Process.WriteMemory(shellcodeBuffer, shellcode);

            // Create a thread to execute the shellcode

            var ntStatus = Ntdll.NtCreateThreadEx(out var threadHandle, AccessMask.SpecificRightsAll | AccessMask.StandardRightsAll, IntPtr.Zero, Process.SafeHandle, shellcodeBuffer, IntPtr.Zero, ThreadCreationFlags.HideFromDebugger | ThreadCreationFlags.SkipThreadAttach, IntPtr.Zero, 0, 0, IntPtr.Zero);

            if (ntStatus != NtStatus.Success)
            {
                throw ExceptionBuilder.BuildWin32Exception("NtCreateThreadEx", ntStatus);
            }

            if (Kernel32.WaitForSingleObject(threadHandle, int.MaxValue) == -1)
            {
                throw ExceptionBuilder.BuildWin32Exception("WaitForSingleObject");
            }

            threadHandle.Dispose();

            Process.FreeMemory(shellcodeBuffer);

            try
            {
                return(Process.ReadStructure <TStructure>(returnBuffer));
            }

            finally
            {
                Process.FreeMemory(returnBuffer);
            }
        }
Ejemplo n.º 12
0
        private async void RunMessageInBackground(Message message)
        {
            if (message.DeliverAt.HasValue && message.DeliverAt > DateTime.UtcNow)
            {
                await Task.Delay(message.DeliverAt.Value - DateTime.UtcNow);
            }
            else
            {
                await Task.Yield();
            }

            var ct = CancellationToken.None;

            if (message.IsEvent)
            {
                var serviceId = Serializer.Deserialize <ServiceId>(message[nameof(ServiceId)]);
                var eventId   = Serializer.Deserialize <EventId>(message[nameof(EventId)]);
                var eventDesc = new EventDescriptor {
                    EventId = eventId, ServiceId = serviceId
                };
                var subscribers = DataStore.GetEventSubscribers(eventDesc);

                foreach (var subscriber in subscribers)
                {
                    var routineId = Interlocked.Increment(ref DataStore.RoutineCounter);

                    var routineRecord = new RoutineStateRecord
                    {
                        ETag       = DateTime.UtcNow.Ticks.ToString("X16"),
                        Id         = routineId.ToString(),
                        Completion = new TaskCompletionSource <string>()
                    };

                    lock (DataStore.Routines)
                    {
                        DataStore.Routines.Add(routineRecord.Id, routineRecord);
                    }

                    var transitionDescriptor = new TransitionDescriptor
                    {
                        Type = TransitionType.InvokeRoutine,
                        ETag = routineRecord.ETag
                    };

                    var routineDescriptor = new RoutineDescriptor
                    {
                        MethodId  = subscriber.MethodId,
                        IntentId  = _numericIdGenerator.NewId(),
                        RoutineId = routineRecord.Id,
                        ETag      = routineRecord.ETag
                    };

                    var invokeRoutineMessage = new Message
                    {
                        //["IntentId"] = _serializer.Serialize(intent.Id),
                        [nameof(TransitionDescriptor)] = Serializer.SerializeToString(transitionDescriptor),
                        [nameof(ServiceId)]            = Serializer.SerializeToString(subscriber.ServiceId),
                        [nameof(RoutineDescriptor)]    = Serializer.SerializeToString(routineDescriptor),
                        ["Parameters"] = message["Parameters"]
                    };

                    DataStore.ScheduleMessage(invokeRoutineMessage);
                }
            }
            else
            {
                for (; ;)
                {
                    var carrier = new TransitionCarrier(this, message);
                    carrier.Initialize();

                    //var transitionInfo = await data.GetTransitionDescriptorAsync(ct);
                    //if (transitionInfo.Type == TransitionType.InvokeRoutine ||
                    //    transitionInfo.Type == TransitionType.ContinueRoutine)
                    //{
                    //    var routineDescriptor = await data.GetRoutineDescriptorAsync(ct);

                    //    if (!string.IsNullOrEmpty(transitionInfo.ETag) &&
                    //        transitionInfo.ETag != routineDescriptor.ETag)
                    //    {
                    //        // Ignore - stale duplicate message
                    //        return;
                    //    }
                    //}

                    try
                    {
                        await _transitionRunner.RunAsync(carrier, ct);

                        break;
                    }
                    catch (ConcurrentRoutineExecutionException)
                    {
                        // re-try
                        continue;
                    }
                }
            }
        }
Ejemplo n.º 13
0
        internal void CallRoutine(IntPtr functionAddress, params dynamic[] parameters)
        {
            var routineDescriptor = new RoutineDescriptor(functionAddress, parameters, IntPtr.Zero);

            CallRoutine(routineDescriptor);
        }
Ejemplo n.º 14
0
        internal static ReadOnlyMemory <byte> AssembleRoutine(RoutineDescriptor routineDescriptor)
        {
            var routineInstructions = new List <byte>();

            void AssembleStackParameters(IEnumerable <long> parameters)
            {
                foreach (var parameter in parameters)
                {
                    if (parameter <= sbyte.MaxValue)
                    {
                        // push parameter

                        routineInstructions.AddRange(new byte[] { 0x6A, (byte)parameter });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // push parameter

                        routineInstructions.Add(0x68);

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov rax, parameter

                        routineInstructions.AddRange(new byte[] { 0x48, 0xB8 });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));

                        // push rax

                        routineInstructions.Add(0x50);
                    }
                }
            }

            if (routineDescriptor.Architecture == Architecture.X86)
            {
                if (routineDescriptor.CallingConvention == CallingConvention.FastCall)
                {
                    if (routineDescriptor.Parameters.Length > 0)
                    {
                        var parameter = routineDescriptor.Parameters[0];

                        if (parameter == 0)
                        {
                            // xor ecx, ecx

                            routineInstructions.AddRange(new byte[] { 0x31, 0xC9 });
                        }

                        else
                        {
                            // mov ecx, parameter

                            routineInstructions.Add(0xB9);

                            routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                        }
                    }

                    if (routineDescriptor.Parameters.Length > 1)
                    {
                        var parameter = routineDescriptor.Parameters[1];

                        if (parameter == 0)
                        {
                            // xor edx, edx

                            routineInstructions.AddRange(new byte[] { 0x31, 0xD2 });
                        }

                        else
                        {
                            // mov edx, parameter

                            routineInstructions.Add(0xBA);

                            routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                        }
                    }

                    if (routineDescriptor.Parameters.Length > 2)
                    {
                        var list = routineDescriptor.Parameters.ToList();
                        //AssembleStackParameters(routineDescriptor.Parameters[2..]);
                        AssembleStackParameters(list.GetRange(2, list.Count() - 2));
                    }
                }

                else
                {
                    AssembleStackParameters(routineDescriptor.Parameters);
                }

                // mov eax, functionAddress

                routineInstructions.Add(0xB8);

                routineInstructions.AddRange(BitConverter.GetBytes(routineDescriptor.FunctionAddress.ToInt32()));

                // call eax

                routineInstructions.AddRange(new byte[] { 0xFF, 0xD0 });

                // mov [returnBuffer], eax

                routineInstructions.Add(0xA3);

                routineInstructions.AddRange(BitConverter.GetBytes(routineDescriptor.ReturnBuffer.ToInt32()));
            }

            else
            {
                var shadowSpace = routineDescriptor.Parameters.Length > 4 ? (routineDescriptor.Parameters.Length * sizeof(long) + 15) & -16 : 40;

                // sub rsp, shadowSpace

                routineInstructions.AddRange(new byte[] { 0x48, 0x83, 0xEC, (byte)shadowSpace });

                if (routineDescriptor.Parameters.Length > 0)
                {
                    var parameter = routineDescriptor.Parameters[0];

                    if (parameter == 0)
                    {
                        // xor ecx, ecx

                        routineInstructions.AddRange(new byte[] { 0x31, 0xC9 });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // mov ecx, parameter

                        routineInstructions.Add(0xB9);

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov rcx, parameter

                        routineInstructions.AddRange(new byte[] { 0x48, 0xB9 });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));
                    }
                }

                if (routineDescriptor.Parameters.Length > 1)
                {
                    var parameter = routineDescriptor.Parameters[1];

                    if (parameter == 0)
                    {
                        // xor edx, edx

                        routineInstructions.AddRange(new byte[] { 0x31, 0xD2 });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // mov edx, parameter

                        routineInstructions.Add(0xBA);

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov rdx, parameter

                        routineInstructions.AddRange(new byte[] { 0x48, 0xBA });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));
                    }
                }

                if (routineDescriptor.Parameters.Length > 2)
                {
                    var parameter = routineDescriptor.Parameters[2];

                    if (parameter == 0)
                    {
                        // xor r8d, r8d

                        routineInstructions.AddRange(new byte[] { 0x45, 0x31, 0xC0 });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // mov r8d, parameter

                        routineInstructions.AddRange(new byte[] { 0x41, 0xB8 });

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov r8, parameter

                        routineInstructions.AddRange(new byte[] { 0x49, 0xB8 });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));
                    }
                }

                if (routineDescriptor.Parameters.Length > 3)
                {
                    var parameter = routineDescriptor.Parameters[3];

                    // mov r9, parameter

                    if (parameter == 0)
                    {
                        // xor r9d, r9d

                        routineInstructions.AddRange(new byte[] { 0x45, 0x31, 0xC9 });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // mov r9d, parameter

                        routineInstructions.AddRange(new byte[] { 0x41, 0xB9 });

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov r9, parameter

                        routineInstructions.AddRange(new byte[] { 0x49, 0xB9 });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));
                    }
                }

                if (routineDescriptor.Parameters.Length > 4)
                {
                    var list = routineDescriptor.Parameters.ToList();
                    //AssembleStackParameters(routineDescriptor.Parameters[4..]);
                    AssembleStackParameters(list.GetRange(4, list.Count() - 4));
                }

                // mov rax, functionAddress

                routineInstructions.AddRange(new byte[] { 0x48, 0xB8 });

                routineInstructions.AddRange(BitConverter.GetBytes(routineDescriptor.FunctionAddress.ToInt64()));

                // call rax

                routineInstructions.AddRange(new byte[] { 0xFF, 0xD0 });

                // mov [returnBuffer], rax

                routineInstructions.AddRange(new byte[] { 0x48, 0xA3 });

                routineInstructions.AddRange(BitConverter.GetBytes(routineDescriptor.ReturnBuffer.ToInt64()));

                // add rsp, shadowSpace

                routineInstructions.AddRange(new byte[] { 0x48, 0x83, 0xC4, (byte)shadowSpace });
            }

            // ret

            routineInstructions.Add(0xC3);

            return(routineInstructions.ToArray());
        }
Ejemplo n.º 15
0
        internal static ReadOnlyMemory <byte> AssembleRoutine(RoutineDescriptor routineDescriptor)
        {
            var routineInstructions = new List <byte>();

            void AssembleStackParameters(IEnumerable <long> parameters)
            {
                foreach (var parameter in parameters)
                {
                    if (parameter <= sbyte.MaxValue)
                    {
                        // push parameter

                        routineInstructions.AddRange(new byte[] { 0x6A, (byte)parameter });
                    }

                    else if (parameter <= uint.MaxValue)
                    {
                        // push parameter

                        routineInstructions.Add(0x68);

                        routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                    }

                    else
                    {
                        // mov rax, parameter

                        routineInstructions.AddRange(new byte[] { 0x48, 0xB8 });

                        routineInstructions.AddRange(BitConverter.GetBytes(parameter));

                        // push rax

                        routineInstructions.Add(0x50);
                    }
                }
            }

            if (routineDescriptor.Architecture == Architecture.X86)
            {
                if (routineDescriptor.CallingConvention == CallingConvention.FastCall)
                {
                    if (routineDescriptor.Parameters.Length > 0)
                    {
                        var parameter = routineDescriptor.Parameters[0];

                        if (parameter == 0)
                        {
                            // xor ecx, ecx

                            routineInstructions.AddRange(new byte[] { 0x31, 0xC9 });
                        }

                        else
                        {
                            // mov ecx, parameter

                            routineInstructions.Add(0xB9);

                            routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                        }
                    }

                    if (routineDescriptor.Parameters.Length > 1)
                    {
                        var parameter = routineDescriptor.Parameters[1];

                        if (parameter == 0)
                        {
                            // xor edx, edx

                            routineInstructions.AddRange(new byte[] { 0x31, 0xD2 });
                        }

                        else
                        {
                            // mov edx, parameter

                            routineInstructions.Add(0xBA);

                            routineInstructions.AddRange(BitConverter.GetBytes((int)parameter));
                        }
                    }

                    if (routineDescriptor.Parameters.Length > 2)
                    {
                        AssembleStackParameters(routineDescriptor.Parameters[2..]);