12

I have an issue where I need to be able to have a compiled exe ( .net 3.5 c# ) that I will make copies of to distribute that will need to change a key for example before the exe is sent out.

I cannot compile each time a new exe is needed. This is a thin client that will be used as part of a registration process.

Is it possible to add a entry to a resource file with a blank value then when a request comes in have another application grab the blank default thin client, copy it, populate the blank value with the data needed.

If yes how? If no do you have any ideas? I have been scratching my head for a few days now and the limitation as due to the boundaries I am required to work in.

The other idea I has was to inject the value into a method, which I have no idea how I would even attempt that.

Thanks.

nitefrog
  • 1,760
  • 6
  • 31
  • 59
  • What is the purpose of this? Knowing that might help us help you better. – Robert Harvey Apr 30 '10 at 06:06
  • Hello Robert, I need to distribute a thin client exe to x users. This client however need to be able to have a key injected on the fly before it is sent. Compiling on demand will not work. Every client sent out will have its own unique identifier associated with it. I cant use an app.config file as the file must be self contained ( another requirement ) hence I started looking at resource file as the key must be embedded. Thanks. – nitefrog Apr 30 '10 at 07:17
  • Will the file have to be signed? – Lasse V. Karlsen Apr 30 '10 at 13:46
  • I think it might be, but if you have an option I am all ears. Thanks. – nitefrog Apr 30 '10 at 16:53

6 Answers6

6

Convert the assembly to IL, do a textual search and replace, recompile the IL to an assembly again. Use the standard tools from the .NET SDK.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • Dan and Amry I just posted this question: http://stackoverflow.com/questions/2749113/compile-il-code-at-runtime-using-net-3-5-and-c-from-file I am going to take the ildasm and ilasm approach, but do you know how to automate this without having to command line it using process.start? – nitefrog May 01 '10 at 06:42
6

Instead of embedding the key in the assembly, put it in the app.config file (or another file delivered with the application) and prevent your application from running if the key is not present and valid. To protect it against modification by users, also add an RSA signature the config file.

This code could be used to generate XML containing your key.

public static void Main()
{
   Console.WriteLine(GenerateKey());
}

public static Byte[] Transform(Byte[] bytes, ICryptoTransform xform)
{
   using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
   {
      using (CryptoStream cstream = new CryptoStream(stream, xform, CryptoStreamMode.Write))
      {
         cstream.Write(bytes, 0, bytes.Length);
         cstream.Close();
         stream.Close();
         return stream.ToArray();
      }
   }
}

public static string GenerateKey()
{
   RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
   // This is the private key and should never be shared.
   // Generate your own with RSA.Create().ToXmlString(true).
   String rsaPrivateKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent><P>4SMSdNcOP0qAIoT2qzODgyl5yu9RubpIU3sSqky+85ZqJHXLUDjlgqAZvT71ROexJ4tMfMOgSWezHQwKWpz3sw==</P><Q>0krr7cmorhWgwCDG8jmzLMo2jafAy6tQout+1hU0bBKAQaPTGGogPB3hTnFIr84kHcRalCksI6jk4Xx/hiw+sw==</Q><DP>DtR9mb60zIx+xkdV7E8XYaNwx2JeUsqniwA3aYpmpasJ0N8FhoJI9ALRzzp/c4uDiuRNJIbKXyt6i/ZIFFH0qw==</DP><DQ>mGCxlBwLnhkN4ind/qbQriPYY8yqZuo8A9Ggln/G/IhrZyTOUWKU+Pqtx6lOghVdFjSxbapn0W8QalNMFGz7AQ==</DQ><InverseQ>WDYfqefukDvMhPHqS8EBFJFpls/pB1gKsEmTwbJu9fBxN4fZfUFPuTnCIJsrEsnyRfeNTAUFYl3hhlRYZo5GiQ==</InverseQ><D>qB8WvAmWFMW67EM8mdlReI7L7jK4bVf+YXOtJzVwfJ2PXtoUI+wTgH0Su0IRp9sR/0v/x9HZlluj0BR2O33snQCxYI8LIo5NoWhfhkVSv0QFQiDcG5Wnbizz7w2U6pcxEC2xfcoKG4yxFkAmHCIkgs/B9T86PUPSW4ZTXcwDmqU=</D></RSAKeyValue>";

   rsa.FromXmlString(rsaPrivateKey);
   String signedData = "<SignedData><Key>Insert your key here</Key></SignedData>";
   Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(signedData);
   Byte[] sigBytes = rsa.SignData(licenseData, new SHA1CryptoServiceProvider());
   String sigText = System.Text.Encoding.UTF8.GetString(Transform(sigBytes, new ToBase64Transform()));
   System.Text.StringBuilder sb = new StringBuilder();
   using (System.Xml.XmlWriter xw = System.Xml.XmlTextWriter.Create(sb))
   {
      xw.WriteStartElement("License");
      xw.WriteRaw(signedData);
      xw.WriteElementString("Signature", sigText);
      xw.WriteEndElement();
   }
   return sb.ToString();
}

Example output from this code:

<?xml version="1.0" encoding="utf-16"?>
<License>
  <SignedData>
    <Key>Insert your key here</Key>
  </SignedData>
  <Signature>cgpmyqaDlHFetCZbm/zo14NEcBFZWaQpyHXViuDa3d99AQ5Dw5Ya8C9WCHbTiGfRvaP4nVGyI+ezAAKj287dhHi7l5fQAggUmh9xTfDZ0slRtvYD/wISCcHfYkEhofXUFQKFNItkM9PnOTExZvo75pYPORkvKBF2UpOIIFvEIU=</Signature>
</License>

Then you can use code like this to verify it. You never have to distribute the private key:

public static Boolean CheckLicenseSignature(String licXml)
{
   try
   {
      System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
      xd.LoadXml(licXml);
      String licSig = xd.SelectSingleNode("/License/Signature").InnerText;
      RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
      String rsaPublicKey = "<RSAKeyValue><Modulus>uPCow37yEzlKQXgbqO9E3enSOXY1MCQB4TMbOZyk9eXmc7kuiCMhJRbrwild0LGO8KE3zci9ETBWVVSJEqUqwtZyfUjvWOLHrf5EmzribtSU2e2hlsNoB2Mu11M0SaGd3qZfYcs2gnEnljfvkDAbCyJhUlxmHeI+35w/nqSCjCk=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
      rsa.FromXmlString(rsaPublicKey);
      Byte[] licenseData = System.Text.Encoding.UTF8.GetBytes(xd.SelectSingleNode("/License/SignedData").OuterXml);
      return rsa.VerifyData(licenseData, new SHA1CryptoServiceProvider(), Transform(System.Text.Encoding.UTF8.GetBytes(licSig), new FromBase64Transform()));
   }
   catch (System.Xml.XmlException ex)
   {
      return false;
   }
   catch (InvalidOperationException ex)
   {
      return false;
   }
}
BlueMonkMN
  • 25,079
  • 9
  • 80
  • 146
  • 1
    Again the file must be self contained. I cannot have any dependent files, but this is a pretty awesome solution if I could have another file to ride along. Also the key must be able to be read as encryption isn't needed. There is another process in the security that will lock down security. I just need to have a unique identifier associated with the exe and it needs to be embedded with each exe I send out. – nitefrog Apr 30 '10 at 18:40
  • I thought you might be solving the wrong problem because I think in many cases the app's config file is automatically transported with the executable. And there are also ways of packaging multiple files into one if necessary. But if those problems are harder to deal with than the solution you've accepted, certainly take the path of least resistance. – BlueMonkMN May 02 '10 at 12:37
  • Also, the key does not get encrypted by this code. The entire XML is plain text, and only the signature added to the end is incomprehensible. – BlueMonkMN May 02 '10 at 12:38
1

From within the capability of the .NET code itself, I'm not sure if this is doable. But it is possible to dynamically generate a .NET DLL which contains some key that can be referred from the main application. That is, if you wouldn't mind a second file in the distribution.

Or if you don't mind to use Ildasm to disassemble the .exe, change the key, then use Ilasm to reassemble, then you can do something to automate that.

Amry
  • 4,791
  • 2
  • 23
  • 24
  • If you convert to il and convert back will that cause any issues with signing the exe once it is converted back. The process, needs to be automated. For the life of me I cannot get UpdateResource to work. It modifies the file but does not overwrite the Name value... – nitefrog Apr 30 '10 at 07:54
  • You can sign the exe when reassembling it using the ilasm /key=keyfile argument. – Amry Apr 30 '10 at 08:01
  • I just tried and created an IL, the issue is that the resource file info does not show anywhere. I can see the name but not the value. Has anyone gotten UpdateResource to work? – nitefrog Apr 30 '10 at 08:19
1

I don't think You can get away without recompiling Your .exe and having key embedded into said .exe. The compilation process can be automated though via use of ildasm.exe and ilasm.exe as Daniel Earwicker suggested in his response https://stackoverflow.com/a/2742902/2358659

I'd like to expand on that if anyone else stumbles across this topic in the future.

I recently was facing similar problem due to my poor source code version control habits. In a nutshell I had an executable that was supposed to write some data to a Google Spreadsheet by referencing it's ID. Long after executable was released came another request from a different team to use the tool, but it had to write same information into a different spreadsheet in order to keep data separate for two teams. At the time I did not have the original source code, hence I was not able to change the static variable holding the original spreadsheet ID. What I did was as follows:

  1. Using CMD.exe → call "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\ildasm.exe" "myApplication.exe" /out="myApplication.il"
  2. Using Notepad++ → Find and replace original ID to new ID inside myApplication.il file. This action can also be automated by writing own C# application to do this, or using PowerShell, or using vb/j-script or using some other find and replace tool available off-the-shelf, like FART (using CMD.exe → call fart.exe myApplication.il "OldKey" "NewKey")
  3. Using CMD.exe → call "C:\Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe" "myApplication.il" /res="myApplication.res" /key="myApplicationKeyFile.snk"

As You see, all of these steps can be put into one .bat file that takes "NewKey" as an input and produces new .exe with NewKey embedded.

I hope that helps.

Community
  • 1
  • 1
Maz T
  • 1,184
  • 13
  • 18
1

The accepted answer is GARBAGE! I HAVE DONE THIS SUCCESSFULLY. MUCH EASIER

Just put your base application (.net) that needs the key somewhere with a string resource FILLED WITH "XXXXXXXXXXXXXXX" (more than you'll need)

.Net resources are usually kept at the top of the code so you will find them fast skipping the first 100,000 bytes in my case.

Then you just read it in and look for those XXXXXX's. When you find them you replace them with the real API key and replace the rest of the X's with spaces you just trim off in code. This is the answer. It works and it works well.

        ApiToken at = new ApiToken(UserId, SelectedCID);
        at.MakeToken();

        byte[] app = System.IO.File.ReadAllBytes(Path.Combine(AppDomain.CurrentDomain.GetData("DataDirectory").ToString(), "notkeyedapp.exe"));

        for (int i = 100000; i < app.Length; i++)
        {
            if (app[i] == 0x58 && app[i + 1] == 0x58 && app[i + 2] == 0x58)
            {
                for (int j = 0; j < 128; j++)
                {
                    if (at.Token.Length >= j + 1)
                        app[i + j] = System.Text.Encoding.ASCII.GetBytes(at.Token[j].ToString())[0];
                    else
                        app[i + j] = 0x20;

                }
                break;
            }
        }
        string filename = "SoftwareProduct for - " + BaseModel.CompanyName.Replace(".", "") + ".exe";
        return File(app, System.Net.Mime.MediaTypeNames.Application.Octet, filename);
8vtwo
  • 60
  • 7
-1

What comes to my mind, but not tried yet: Create a default String in your program, for example as

static public string regGuid = "yourguidhere";

Then, search the compiled EXE with any decent hex editor. If you find the string, replace it with another test. If you still can execute the program, you could try to automate this process and voila! Here you are.

Marcel
  • 15,039
  • 20
  • 92
  • 150
  • 2
    I don't know if unsigned assemblies have any sort of checksum, but I think that will fail for signed assemblies, as the signature will no longer be valid. – Jon Skeet Apr 30 '10 at 06:12
  • That is the problem, the key cannot be guaranteed to be the same length every time. Hence a variable length key. – nitefrog Apr 30 '10 at 07:19
  • @nitefrong: As long as you allocate *more* space than the key will need (e.g. 40 characters and just pad the key out with spaces when you insert it), that isn't an issue. But signing will be. – Jason Williams Apr 30 '10 at 07:23
  • Not work. I try this and not find string: public static readonly string ops = "HAHAHA"; – Sergio Cabral Jul 30 '15 at 01:40