As I mention in a comment, this is possible in .NET using the magic System.Reflection.Emit namespace. You simply create a new DynamicMethod and allocate all the [valid] opcodes into it, and then call Invoke.
I spent the last few hours trying to create a simple showcase for a "clean" program that would create new copies of itself with encrypted il code. The approach I went to was the Exec method, grabbed the il-bytes (using MethodBase.GetMethodBody ), encrypted them, and released a new assembly with the iv + key and encrypted bytes. Then the main method will decrypt, create a new DynamicMethod, call DynamicILInfo.SetCode and, hopefully, it will work. This is not true.
The encryption / decryption job worked, and my emitted code was right. However, it seems that you cannot take raw bytes from one assembly and just execute them in another.
Data (from BitConverter.ToString) from start A and run B.
A: 28-01-00-00-0A ...
B: 28-11-00-00-0A ...
If you do not know the byte values ββfor each opcode, open ILDAsm, choose View> Show Bytes. There's also a View> Show token view, which also helps debug. Press ctrl-m to browse> MetaData> Show! to resolve tokens and other magical creatures.
"28 01 00 00 0A" β CALL 0A000001 β [According to ctrl-m] MethodBase.GetCurrentMethod
These different token values ββare generated sequentially by the compiler. This means that it is impossible to guarantee that everything will work using raw bytes. Think about the general case where the compiler just created tokens for each method call, requires decryption of the byte array, and you call Console.WriteLine in your encrypted code. Such a token is not written, and when you call the dynamic method, you will get "BadImageFormatException: Bad binary signature".
I leave this as a task for reading (or until I get bored) to convert the byte array during the output process to a format that the decoder can read and emit into real bytes. The emission process will create all the necessary tokens, so it should work.
If you want to get rid of all the acuteness of emitting opcodes, do some dynamic compilation from code stored as strings (which, of course, can be encrypted). This, however, loses both in intelligence, coolness, and in everything else that can be used to measure the developerβs sheer surprise (YOU!). Check out this tutorial to quickly display dynamic compilation and C # execution in lines.