Encrypt text which comes to StreamWriter from Console.SetOut

kosmosec

Member
Joined
Sep 16, 2021
Messages
18
Programming Experience
5-10
Hi, I run the other assembly from my program and I've redirected the output to the file. I would like to encrypt (at least the simple XOR) output from the other assembly in "real time". I will show this on snippet:

output:
FileStream streamer;
StreamWriter writer;
          
TextWriter oldOut = Console.Out;
TextWriter oldErr = Console.Error;
try
    {
        streamer = new FileStream(fileName, FileMode.OpenOrCreate | FileMode.Append, FileAccess.Write);
        writer = new StreamWriter(streamer);
        writer.AutoFlush = true;
    }
catch (Exception e)
    {
        return;
    }
Console.SetOut(writer);
Console.SetError(writer);
        
try
{
Assembly assembly = Assembly.Load(targetdotnet);
MethodInfo method = assembly.EntryPoint;
object[] pp = new[] { parameters };
object execute = method.Invoke(null, pp);

I'm wondering if I can instrument somehow the StreamWriter to encrypt what it comes.
 
Your can, but it'll be even better if you wrap the stream to encrypt on write and then pass the wrapper stream to the stream writer. In fact, most of the built in encryption with .NET is setup to work with streams.
 
I've done something like this and it works but not in "real time". The CryptoStream doesn't have a property AutoFlush.
crypto:
DESCryptoServiceProvider cryptic = new DESCryptoServiceProvider();

cryptic.Key = ASCIIEncoding.ASCII.GetBytes("ABCDEFGH");
cryptic.IV = ASCIIEncoding.ASCII.GetBytes("ABCDEFGH");

streamer = new FileStream(fileName, FileMode.OpenOrCreate | FileMode.Append, FileAccess.Write);
CryptoStream csEncrypt = new CryptoStream(streamer, cryptic.CreateEncryptor(), CryptoStreamMode.Write);
writer = new StreamWriter(csEncrypt);
writer.AutoFlush = true;
 
That tends to happen if you are using a block cipher or a cipher in block mode. It will only output as each block completes or when the stream is finally closed.
 
The CryptoStream doesn't have a property AutoFlush.
But neither does the FileStream or Stream. Why would that be an issue?
 
But neither does the FileStream or Stream. Why would that be an issue?
The StreamWriter has the AutoFlush. I use it as I show in the first snippet. Without this the buffer will be flushed at the end of the operation. The AutoFlush flushes its buffer after every call to StreamWriter.Write() so I have the output in file in real time and I also need it using crypto.

The block cipher it could be the problem in this case. I thought I will be able to just XOR but seems I will have to implement ICryptoTransform, am I right? Are there any steam cipher in C# which you used before and it could works?
 
That is correct. There is no built in XOR, ROT13, nor Caesar ciphers built into the framework. Those should be relatively easy to implement.

I vaguely recall that the interface itself tends to be block oriented (because most modern ciphers works in blocks to resist cryptanalysis), but I think they can still be called incrementally with non-block sizes inputs.

It maybe easier just to write an XorStream, or Rot13Stream, or CaesarStream class and not even deal with the interface.
 
Ah, I see why the AutoFlush isn't doing anything for you. From the source code for CryptoStream.Flush():
C#:
        public override void Flush() {
            return;
        }

Looks like you'll definitely have to roll your own Stream instead of just implementing an interface for your cipher.
 
Yes, I will have to create Stream which has AutoFlush but tbh I don't know how to do it. I was thinking about custom Encoder which will XOR (yeah.. it is not encoding :D) beacuse I can pass Encoder to StreamWriter constructor.
 
You could, but consider that a Stream has a much smaller interface to implement/override compared to an Encoding. The more methods you write, the more unit tests you have to write as well.
 
I've copy-paste the entire StreamWriter class and changed Flush(bool, bool) method like that.
flush:
[code...]
 if (count > 0)
            {
                
                byte[] xoredBytes = new byte[byteBuffer.Length];
                for (int i = 0; i < byteBuffer.Length; i++)
                {
                    byte xoredInt = (byte)(byteBuffer[i] ^ 0x66);
                    xoredBytes[i] = xoredInt;
                }
              
                stream.Write(xoredBytes, 0, count);
            }

[code...]

It works but some letters are missing and I don't know why :D
 
Missing is highly doubtful since XOR replaces byte per byte as you know. Very likely, the new post XOR'ed values you are generating is simply unprintable or incorrect for the encoding.

For example, you are XORing with 0x66. So when an ASCII 'f' comes along, it now gets written out an ASCII NUL character because 0x66 ^ 0x66 == 0x00. In fact, all the lower case characters will be driven into the non printing ASCII range.

If your encoding is Unicode, then a 'f' that has Unicode 0x0066 will become Unicode 0x6600. Depending on your font choices, there may not be a glyph mapped to that Unicode point.
 
Yes, of course, you're right about the encoding and XOR'ed result. So basically, write data as binary - xor each byte and then pass the byte's array to the stream.Write() and using Encoding.UTF8 should work? Then read data as binary (byte array), xor using the same key then convert to string. I'm sure that data are printable so XORed twice also will be printable.
 
Yes, XORed twice should bring it back as long as you do it in the correct order:

Unicode -> UTF8 -> XOR -> intermediate storage -> XOR -> UTF8 -> Unicode
 
Or better yet, a slightly UTF8 friendlier approach:

Unicode -> XOR -> UTF8 -> intermediate storage -> UTF8 -> XOR -> Unicode

My gut feel is that this will involve more code, though based on my brief scan through the StreamWriter reference source code, as well as, having to add extra code to ensure the Unicode -> XOR step does not generate any values in the Surrogate range (U+D800 to U+DFFF) because a proper Unicode to UTF8 or UTF8 to Unicode converter should reject this as illegal codepoints.
 
Back
Top Bottom