Skip to main content
GameDev.net gamedev.net
🔒 Locked

Securely store a string in a C++ program?

Started by Josh Klint Aug 24, 2024 at 10:39 PM 17 replies 9.7k views
Original Post
Josh Klint
Josh Klint

I am looking for a way to store a string in a C++ program, preferably in code, without making it visible in a hex editor that opens the executable. I can think of many different ways to do this, but if any simple standard way of doing this exists, that would be better. I don't know much about “secure strings” but these seem to be focused on zeroing memory after the string goes out of scope, which is not the primary issue I am trying to solve.

How can I prevent a string from being visible when a program is opened in a hex editor or similar tool?

taby
taby

ROT13 if you want fast and easy. LOL. RSA is another algorithm, but not so fast, and not so easy.

TheBuzzSaw
TheBuzzSaw

If you're main goal is to simply hide from string dumpers, just add a preprocessor step to your project. Put all the readable strings into a JSON file or something. Have a script/program read that file and dump all of them to a C++ source file with the strings already encoded. Then you just need a decode function in your actual program.

Amateurs practice until they do it right.Professionals practice until they never do it wrong.
Alberth
Alberth

Use a pseudo-random number generator with a known start-value or apply a CRC function on the string, and XOR the characters with a part of that number.

JoeJ
JoeJ

Well, any form of encryption as mentioned causes a need for annoying conversations.


So instead this:

vector string props = "health", "xp", "dlc_obtained"

I would use this:

enum props {health, xp, dlc_obtained}
Alberth
Alberth

Ooh !

I thought you had text to display but didn't want it trivially readable (eg in an text adventure game).

For names of properties etc, I fully agree with JoeJ. A number beats any string literal any day.

sevenfold1
sevenfold1

If you just want to hide text, there's nothing wrong with using a simple XOR function:

std::string
XORString(std::string & in) {

char key = 23;

std::string out = in;

for(int i=0; i < in.size(); i++) {
   out[i] = in[i] ^ key;
}

return out;
}

The same function encrypts and decrypts. Key can anything (one or more chars). You didn't mention the word “password”, so this is obviously not secure for that particular purpose. The key is only 1 character, it can easily be reversed by brute-force in less than a second. 🙂

This is a gaming forum. If we're talking about text in game resources and not passwords, then I choose obscurity over security.

LorenzoGatti
LorenzoGatti

If you obfuscate strings with weak encryption (e.g. ROT13) an hex editor or statistical analysis tool can see through it.

If you obfuscate strings with stronger but still insecure means (e.g. any current stream cypher with a key somewhere in your file) you are putting the encrypted blobs in your program in the limelight, guiding even relatively inexpert hackers in their attempts to extract strings.

If you obfuscate strings with annoying techniques (e.g. convoluted assemblage of disparately encoded fragments) the hacker's effort is at most proportional to your own.

So if you want to prevent casual spoilers and editing, minimum effort scrambling is a good choice; if something needs to be secure you cannot put keys in your program in any case. Anything in the middle is just a collateral game you are entertaining hackers with.

Omae Wa Mou Shindeiru
taby
taby

https://github.com/sjhalayka/RSA

frob
frob

Just be aware it still is not “secure”. It is easily extracted by anyone who can run the program and is motivated to obtain it.

Locking it with RSA would help secure the “data at rest” and “data in transit” phases, poking at the values on disk won't reveal what is encrypted without the key, watching it flow over the wire the same, but it still is opened up in the “data in use” phases. Anyone with a debugger could still intercept it, and with analysis could also get the key and unlock it themselves.

Mostly this is about understanding what you are protecting and who you are trying to protect it from. Often all it takes is one motivated person to dig in and publish the answer to expose it to the world. Are you okay with that? Sometimes it is more complex, like a patch where you must be careful against leaking anything around unannounced future features in progress, that might unintentionally be leaked through unexpected binary metadata and discovered by enthusiastic fans. Other times it is about avoiding exploits, or about securing transactions between players, or about attacks on the infrastructure, or exposed player information like public IP addresses, or other information. Fortunately in general games are not protecting critical secrets that impact lives or livelihoods.

JoeJ
JoeJ

Josh Klint said:
The purpose of this is to obfuscate a password for an AES-256 encrypted zip file that holds game files.

You want to have passwords in code for production convenience?

Put it in a header file, add a compile time task to generate an encrypted header file, which is included in the release build.

frob
frob

Josh Klint said:

The purpose of this is to obfuscate a password for an AES-256 encrypted zip file that holds game files.

That's going to be trivial for anyone experienced in attacking software.

Yes it will slow down casual folks, but any serious attacks or significant game will have a crack published within a few hours.

LorenzoGatti
LorenzoGatti

frob said:

Josh Klint said:

The purpose of this is to obfuscate a password for an AES-256 encrypted zip file that holds game files.

That's going to be trivial for anyone experienced in attacking software.

Yes it will slow down casual folks, but any serious attacks or significant game will have a crack published within a few hours.

The enemy can disassemble the game, find routines for AES-256 decryption, run the game under a debugger and look at the key as it is passed to and used by those decryption routines.

There are some tricks to make each of the four steps more difficult and/or annoying and/or time consuming, but culling the inexperienced crackers who give up is balanced by attracting the expert crackers who want to be challenged.

Are you trying to prevent piracy, to prevent hacking the game (e.g. to avoid cheating mods in multiplayer matches), to prevent ripping assets, or something else? Useful techniques for different purposes are quite different.

Omae Wa Mou Shindeiru
Josh Klint
Josh Klint

The goal is to prevent ripping of assets, so third-party authors are able to release assets for our engine without worrying about the content being ripped out of distributed games. Yes, I know texture and mesh data can be read from VRAM, but raw texture data is not that useful in most cases.

frob
frob

The simple renamed zip file is adequate for what you need. Nobody is going to bother decrypting AES. As you described, it is easy enough to pluck it from memory once loaded. Anybody with a modern debugger — freely available from system manufacturers — can rip them. Pluck it from RAM, pluck it from the drivers, pluck it from VRAM, record it through RenderDoc or Pix, pull it out through Windows Debugger, or many other tools, none of what you described is 'secure'.

AES will secure it after it is encoded and only until it is decoded. Your program must decode it to make any use of it, and at that point it is accessible in all kinds of methods to anybody with a debugger, performance analysis tool, or hacking tool.

It feels like the ‘third party authors’ don't have a fear based on reality, but based on the theoretical. What you described gives virtually zero ‘security’. It has virtually nothing to do with the initial question of securely storing a string in a C++ program. Spend your time on features that provide actual value.

a light breeze
a light breeze

There's no point in trying to hide an encryption key in an executable. Encryption keys look like raw noise, so the only way the encryption key will be found in the code is by tracing through the program until you get to the decryption algorithm, at which point you already have access to the decoded encryption key.

That said, if I wanted to hide a password in an executable, here's what I'd do:

  • Create a random string of bytes the same length as my password and store it in my executable.
  • Create a second string by xor-ing each byte in the password with one byte from the previous string of random bytes, and store that it the executable.
  • To get the original password, xor the first string with the second string.

Both strings, looked at independently, contain only raw random noise with no hint as to the password. The only realistic way to get to the password is to trace through the program until the strings are combined to form the password. If the attacker does trace through the program, you're fucked anyway.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.