Modify Emdeded String in C# compiled exe - c#

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.

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.

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;
}
}

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.

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);

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:
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"
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")
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.

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.

Related

System.Security.Cryptography.CryptographicException: Object already exists

It's weird. I had this method to encrypt a string:
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Assert, Unrestricted = true)]
public static string Encrypt(this string stringToEncrypt, string key) {
var cspp = new CspParameters {
KeyContainerName = key,
Flags = CspProviderFlags.UseMachineKeyStore
};
var rsa = new RSACryptoServiceProvider(cspp) {
PersistKeyInCsp = true
};
var bytes = rsa.Encrypt(System.Text.Encoding.UTF8.GetBytes(stringToEncrypt), true);
return BitConverter.ToString(bytes);
}
And this was my client:
private const string EncryptionKey = "pezhman";
static Random random = new Random();
public static int CreateSalt() {
return random.Next(1000, 9999);
}
public void EncryptSomething() {
var salt = CreateSalt();
var plainText = salt + "," + DateTime.Now;
var encryptionSaltKey = EncryptionKey + DateTime.Now.Date;
// here im calling encryptor:
var encryptedValue = plainText.Encrypt(encryptionSaltKey);
}
I was using this in an ASP.NET MVC 4 application. It was working perfectly; but suddenly it stopped working. Actually, in local, I have no problem and it works. But, when I publish my code to the server, I get this error:
System.Security.Cryptography.CryptographicException: Object already
exists.
Do you have any idea what's happening here? I know I can grant access to the key to everyone. What I'm asking is, what just happened at the server? What is changed? What kind of changes can cause the problem?
What I'm asking is, what just happened at the server? What is changed? What kind of changes can cause the problem?
One possibility is the recently released Windows secuirty update MS14-059, although I can't explain the error message you are getting.
Basically, that update completely uninstalls MVC 4.0.0.0 and replaces it with 4.0.0.1 on your server, and it has caused grief for many people with broken builds. Since cryptography might depend on something very specific to the version number of the DLL, you might want to start there. You can prove or disprove this theory by testing your application on a machine without the above security patch installed to see if it starts working again.

Search String takes a long time the first time only?

No shortage of search for string performance questions out there yet I still can not make heads or tails out of what the best approach is.
Long story short, I have committed to moving from 4NT to PowerShell. In leaving the 4NT I am going to miss the console super quick string searching utility that came with it called FFIND. I have decided to use my rudimentary C# programming skills to try an create my own utility to use in PowerShell that is just as quick.
So far search results on a string search in 100's of directories across a few 1000 files, some of which are quite large, are FFIND 2.4 seconds and my utility 4.4 seconds..... after I have ran mine at least once????
The first time I run them FFIND does it near the same time but mine takes over a minute? What is this? Loading of libraries? File indexing? Am I doing something wrong in my code? I do not mind waiting a little longer but the difference is extreme enough that if there is a better language or approach I would rather start down that path now before I get too invested.
Do I need to pick another language to write a string search that will be lighting fast
I have the need to use this utility to search through 1000 of files for strings in web code, C# code, and another propitiatory language that uses text files. I also need to be able to use this utility to find strings in very large log files, MB size.
class Program
{
public static int linecounter;
public static int filecounter;
static void Main(string[] args)
{
//
//INIT
//
filecounter = 0;
linecounter = 0;
string word;
// Read properties from application settings.
string filelocation = Properties.Settings.Default.FavOne;
// Set Args from console.
word = args[0];
//
//Recursive search for sub folders and files
//
string startDIR;
string filename;
startDIR = Environment.CurrentDirectory;
//startDIR = "c:\\SearchStringTestDIR\\";
filename = args[1];
DirSearch(startDIR, word, filename);
Console.WriteLine(filecounter + " " + "Files found");
Console.WriteLine(linecounter + " " + "Lines found");
Console.ReadKey();
}
static void DirSearch(string dir, string word, string filename)
{
string fileline;
string ColorOne = Properties.Settings.Default.ColorOne;
string ColorTwo = Properties.Settings.Default.ColorTwo;
ConsoleColor valuecolorone = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ColorOne);
ConsoleColor valuecolortwo = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), ColorTwo);
try
{
foreach (string f in Directory.GetFiles(dir, filename))
{
StreamReader file = new StreamReader(f);
bool t = true;
int counter = 1;
while ((fileline = file.ReadLine()) != null)
{
if (fileline.Contains(word))
{
if (t)
{
t = false;
filecounter++;
Console.ForegroundColor = valuecolorone;
Console.WriteLine(" ");
Console.WriteLine(f);
Console.ForegroundColor = valuecolortwo;
}
linecounter++;
Console.WriteLine(counter.ToString() + ". " + fileline);
}
counter++;
}
file.Close();
file = null;
}
foreach (string d in Directory.GetDirectories(dir))
{
//Console.WriteLine(d);
DirSearch(d,word,filename);
}
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
If you want to speed up your code run a performance analysis and see what is taking the most time. I can almost guaruntee the longest step here will be
fileline.Contains(word)
This function is called on every line of the file, on every file. Naively searching for a word in a string can taken len(string) * len(word) comparisons.
You could code your own Contains method, that uses a faster string comparison algorithm. Google for "fast string exact matching". You could try using a regex and seeing if that gives you a performance enhancement. But I think the simplest optimization you can try is :
Don't read every line. Make a large string of all the content of the file.
StreamReader streamReader = new StreamReader(filePath, Encoding.UTF8);
string text = streamReader.ReadToEnd();
Run contains on this.
If you need all the matches in a file, then you need to use something like Regex.Matches(string,string).
After you have used regex to get all the matches for a single file, you can iterate over this match collection (if there are any matches). For each match, you can recover the line of the original file by writing a function that reads forward and backward from the match object index attribute, to where you find the '\n' character. Then output that string between those two newlines, to get your line.
This will be much faster, I guarantee it.
If you want to go even further, some things I've noticed are :
Remove the try catch statement from outside the loop. Only use it exactly where you need it. I would not use it at all.
Also make sure your system is running, ngen. Most setups usually have this, but sometimes ngen is not running. You can see the process in process explorer. Ngen generates a native image of the C# managed bytecode so the code does not have to be interpreted each time, but can be run natively. This speeds up C# a lot.
EDIT
Other points:
Why is there a difference between first and subsequent run times? Seems like caching. The OS could have cached the requests for the directories, for the files, for running and loading programs. Usually one sees speedups after a first run. Ngen could also be playing a part here, too, in generating the native image after compilation on the first run, then storing that in the native image cache.
In general, I find C# performance too variable for my liking. If the optimizations suggested are not satisfactory and you want more consistent performance results, try another language -- one that is not 'managed'. C is probably the best for your needs.

How to launch a process which will open a text file in any editor and automatically move cursor to a certain line number?

From c#, I want to launch a process which will open a text file in any editor and automatically move cursor to a certain line number.
I can open a file using
Process.Start(#"c:\myfile.txt");
but I don't know how to move cursor at specific location in that file.
Answer with source code:
yes, I used notepad++
private void openLog() {
try {
// see if notepad++ is installed on user's machine
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
if (nppDir != null) {
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(yourDirectory,fileName );
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, lineNo);
Process.Start(nppExePath, sb.ToString());
} else {
string newPath = #"\\mySharedDrive\notpad++\bin\notepad++.exe";
Process.Start(newPath, #"\\" + filePath + " -n" + lineNo); // take exe from my shared drive
}
} catch (Exception e) {
Process.Start(#"\\" + FilePath); // open using notepad
}
}
Get Notepad++, then you can do this:
var nppDir = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Notepad++", null, null);
var nppExePath = Path.Combine(nppDir, "Notepad++.exe");
var nppReadmePath = Path.Combine(nppDir, "readme.txt");
var line = 20;
var sb = new StringBuilder();
sb.AppendFormat("\"{0}\" -n{1}", nppReadmePath, line);
Process.Start(nppExePath, sb.ToString());
In this example we get install path of n++ from the registry, build path to exe and readme.txt file, opens its own readme.txt file with cursor on line 20.
Using StringBuilder is more efficient than using multiple appends (explanation somewhere on SO).
The solution very heavily depends on which process/editor is opened on your system. That editor would have to have a developer API that you could use to access functionality such as setting ranges and altering the cursor position. For example, if the editor that is opened is Microsoft Word, you would use the Word Interop API to set a selection at a specific position. There is no universal way to do this in 'any editor' since each one has its own API (or no outward facing API at all).
Perhaps you are going this the wrong way. I'm not sure what you are trying to accomplish, but I think it would be alot easier to just open the text file in an editor that belongs to your application. Perhaps another form with a WYSIWYG editor control. That way you have full control on where the cursor will land in that editor. Otherwise, there are just way too many unknowns for anything feasibly workable.

why do I get an access denied exception when accessing a memory mapped file from c# but not from c++

I have a WCF service that uses named pipes. Apparently doesn't use the url for the name. It generates a GUID from the url, then stores it in a memory mapped file. So I wrote a C++ app that gets the name of the pipe from the memory mapped file, and that works great. Now I'm trying to make a c# app that gets the name of the pipe from the memory mapped file. here is the code that I'm using.
private static string GetPipeName(string mappedFileName)
{
var mappedFile = MemoryMappedFile.OpenExisting(mappedFileName, MemoryMappedFileRights.Read);
var bytes = new List<byte>();
using (MemoryMappedViewStream s = mappedFile.CreateViewStream())
{
using (var b = new BinaryReader(s))
{
bytes = b.ReadBytes((int)b.BaseStream.Length).ToList();
}
}
var sb = new StringBuilder();
foreach (var b in bytes)
{
sb.Append(b.ToString("x2"));
sb.Append(" ");
}
Console.WriteLine(sb.ToString());
return sb.ToString();
}
At the first using statement I get an UnauthorizedAccessException, that says "Access to the path is denied."
I was able to step through and confirm that the filename is the same that I use in the c++ app and that shows when I use the SysInternals tool Handle.exe
Why would the C# app get an access denied? As far as I can tell both are running as the same user.
you need to use the following
CreateViewAccessor(Int64, Int64, MemoryMappedFileAccess);

C# SAPI in a web service

var speechEngine = new SpVoiceClass();
SetVoice(speechEngine, job.Voice);
var fileMode = SpeechStreamFileMode.SSFMCreateForWrite;
var fileStream = new SpFileStream();
try
{
fileStream.Open(filePath, fileMode, false);
speechEngine.AudioOutputStream = fileStream;
speechEngine.Speak(job.Script, SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak | SpeechVoiceSpeakFlags.SVSFDefault); //TODO: Change to XML
//Wait for 15 minutes only
speechEngine.WaitUntilDone((uint)new TimeSpan(0, 15, 0).TotalMilliseconds);
}
finally
{
fileStream.Close();
}
This exact code works in a WinForm app, but when I run it inside a webservice I get the following
System.Runtime.InteropServices.COMException was unhandled
Message="Exception from HRESULT: 0x80045003"
Source="Interop.SpeechLib"
ErrorCode=-2147201021
Does anyone have any ideas what might be causing this error? The error code means
SPERR_UNSUPPORTED_FORMAT
For completeness here is the SetVoice method
void SetVoice(SpVoiceClass speechEngine, string voiceName)
{
var voices = speechEngine.GetVoices(null, null);
for (int index = 0; index < voices.Count; index++)
{
var currentToken = (SpObjectToken)voices.Item(index);
if (currentToken.GetDescription(0) == voiceName)
{
speechEngine.SetVoice((ISpObjectToken)currentToken);
return;
}
}
throw new Exception("Voice not found: " + voiceName);
}
I have given full access to USERS on the folder C:\Temp where the file is to be written. Any help would be appreciated!
I don't think the System.Speech works in windows service. It looks like there is a dependency to Shell, which isn't available to services. Try interop with SAPI's C++ interfaces. Some class in System.Runtime.InteropServices may help on that.
Our naming convention requires us to use a non-standard file extension. This works fine in a Winforms app, but failed on our web server. Changing the file extension back to .wav solved this error for us.
Make sure you explicitly set the format on the SPFileStream object. ISpAudio::SetState (which gets called in a lower layer from speechEngine.Speak) will return SPERR_UNSUPPORTED_FORMAT if the format isn't supported.
I just got the webservice to spawn a console app to do the processing. PITA :-)

Categories

Resources