Currently working on a stronger version of XorStr and was also interested in just making my own for pure educational purposes, which is why in the code noted below I have simplified the actual algorithm used in the function to better aid in debugging this weird issue. However despite making countless changes that do seem to effect parts of what does and doesn't get encrypted on runtime, my lack of knowledge in this area of C/C++ is really making this challenging so I figured I would ask here.
The Actual Issue: Two strings completely unique to any inside of the program (I verified) are showing different results once compiled for seemingly no reason?
Reproducible Example:
#include <Windows.h>
#include <iostream>
#include <random>
template<size_t Size, int64_t Key, int32_t UniqueID, bool InlinedKey = true>
class ISE {
private:
char StringData[Size];
mutable char DecryptedData[Size];
constexpr static int64_t XorKey = InlinedKey ? Key : 0;
constexpr __forceinline char XorChar(char C, size_t I, size_t S) const {
char KeyCalc1 = (char)((Key + I + S) % 0xFF);
char KeyCalc2 = (char)((I + S) % 0xFF);
char BaseCalc1 = C ^ KeyCalc1;
BaseCalc1 = BaseCalc1 + KeyCalc1;
return BaseCalc1 ^ KeyCalc2;
}
public:
constexpr ISE(const char(&Input)[Size]) : StringData{} {
for (size_t i = 0; i < Size; ++i) {
DecryptedData[i] = '\0';
StringData[i] = XorChar(Input[i], i, Size);
}
}
static const ISE& GetInstance(const char(&Input)[Size]) {
static const ISE OutputInstance(Input);
return OutputInstance;
}
const char* c_str(int64_t InputKey = XorKey) const {
for (size_t i = 0; i < Size; ++i) {
DecryptedData[i] = '\0'; //UXorChar(InputKey, StringData[i], i, Size);
}
return DecryptedData;
}
};
#define UNIQUE_ID (__LINE__ * 0x100 + __COUNTER__)
#define _(Str, Key) (ISE<sizeof(Str), Key, UNIQUE_ID, true>::GetInstance(Str).c_str())
int main()
{
printf(_("Hello", 0xDDDA2254D545));
printf(_("Couldn't Allocate On Target Process, Contact Support!", 0xFFFA2254D545));
return 1;
} //Important to Note the String is being left in the binary, and will likely appear encrypted when running because its just placing my ISE Initializer in the runtime executable (Which it should not be doing!)
That was just one of the examples I could find of a string being left behind, in that example "Hello" is being hidden completely from the binary, but "Couldn't Allocate On Target Process, Contact Support!" is not? When trying this on a larger project it gets much worse, where randomly nearly 30% of the strings appear to not even be processed on compile time and I have no clue why this might be. Any and all help is highly appreciated!
Compiler generates the encrypted string until it can during template instantiation calculate the outcome of your loop with the
XorCharcall which is hard to do for largeSizevalues.When it is possible for the compiler, it generates the resulting encrypted string at compile time, so your source string is no more needed.
When it reaches some threshold (compiler-depended, for MSVC it is 36), it gives up to calculate the outcome at compile time and starts to implement a function which will be actually called with the actual value of the string, so it has to be somewhere stored.
You may see this in the generated code.