Modify Emdeded String in C# compiled exe
Asked Answered
W

6

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.

Walkerwalkietalkie answered 30/4, 2010 at 6:0 Comment(4)
What is the purpose of this? Knowing that might help us help you better.Pygmy
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.Walkerwalkietalkie
Will the file have to be signed?Fluxmeter
I think it might be, but if you have an option I am all ears. Thanks.Walkerwalkietalkie
F
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.

Fracas answered 30/4, 2010 at 7:21 Comment(1)
Dan and Amry I just posted this question: #2749613 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?Walkerwalkietalkie
F
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;
   }
}
Fiesta answered 30/4, 2010 at 13:45 Comment(3)
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.Walkerwalkietalkie
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.Fiesta
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.Fiesta
R
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.

Romney answered 30/4, 2010 at 7:19 Comment(3)
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...Walkerwalkietalkie
You can sign the exe when reassembling it using the ilasm /key=keyfile argument.Romney
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?Walkerwalkietalkie
H
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://mcmap.net/q/584583/-modify-emdeded-string-in-c-compiled-exe

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.

Honan answered 17/3, 2017 at 15:26 Comment(0)
U
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);
Up answered 5/11, 2021 at 0:1 Comment(0)
N
-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.

Nitrification answered 30/4, 2010 at 6:10 Comment(4)
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.Nonstriated
That is the problem, the key cannot be guaranteed to be the same length every time. Hence a variable length key.Walkerwalkietalkie
@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.Diphtheria
Not work. I try this and not find string: public static readonly string ops = "HAHAHA";Mahan

© 2022 - 2025 — McMap. All rights reserved.