Coś nie tak z szyfrowaniem stringów w czasie kompilacji

1

Mam szablon klasy, która umożliwia szyfrowanie stringów w czasie kompilacji:

 
#ifndef ENCRYPTEDSTRING_H_INCLUDED
#define ENCRYPTEDSTRING_H_INCLUDED

#include "CompileTimeRandom.h"

template <unsigned int...> struct IndexList {};
template <typename IndexList, unsigned int Right> struct Append;
template <unsigned int... Left, unsigned int Right> struct Append<IndexList<Left...>, Right> { typedef IndexList<Left..., Right> Result; };
template <unsigned int N> struct Indexes { typedef typename Append<typename Indexes<N - 1>::Result, N - 1>::Result Result; };
template <> struct Indexes<0> { typedef IndexList<> Result; };

const char EncryptCharKey = COMPILETIME_RANDOM(0, 0xFF);
constexpr char EncryptChar(const char c, const unsigned int i) { return c ^ (EncryptCharKey + i); }

template <typename IndexList> struct EncryptedString;
template <unsigned int... I>  struct EncryptedString< IndexList<I...>>
{
public:
    constexpr inline EncryptedString(const char* const str) : buffer({ EncryptChar(str[I], I)...}), isEncrypted(true) { }

    char* Decrypt()
    {
        if(!isEncrypted) return buffer;

        for(volatile unsigned int t = 0; t < sizeof...(I); t++)
            buffer[t] = buffer[t] ^ (EncryptCharKey + t);
        buffer[sizeof...(I)] = '\0';

        isEncrypted = false;
        return buffer;
    }

private:
    char buffer[sizeof...(I) + 1];
    bool isEncrypted;
};

#define ENCRYPT(str) EncryptedString<Indexes<sizeof(str) - 1>::Result>(str).Decrypt()

#endif // ENCRYPTEDSTRING_H_INCLUDED

Z nie wiadomych mi przyczyn szyfrowanie nie zawsze odbywa się w czasie kompilacji, przykładowo fragment kodu poniżej działa poprawnie:

#include "EncryptedString.h"
#include <string>
using namespace std;

int main()
{
    string str = ENCRYPT("1234567890");
}
 

Ale gdy już spróbuje tak:

#include "EncryptedString.h"
#include <string>
using namespace std;

class Test
{
    static string Str;
};
string Test::Str = ENCRYPT("ABCDEFGH");

int main()
{
    string str = ENCRYPT("12345678");
}

To szyfrowanie nie działa, oba stringi można odczytać z pliku exe zwykłym notatnikiem. Zauważyłem że dzieje się tak tylko wtedy kiedy oba szyfrowane stringi mają jednakową długość, kiedy są różnej długości szyfrowanie odbywa się w czasie kompilacji. Czym to może być spowodowane?

0

Sprawdź jak działa sizeof(char *).

0

string alokuje pamięć dynamicznie - nie ma szans, żeby zadziałało to w czasie kompilacji

0
vpiotr napisał(a):

Sprawdź jak działa sizeof(char *).

To raczej nie wina sizeof(), jest to operator który oblicza wartość w czasie kompilacji. Pewnie chodzi Ci o to, że wskaźnik waży (zwykle) 4b, ale jak przekażemy do sizeof() c-string to otrzymujemy wagę całego napisu a nie wskaźnika: http://ideone.com/Y3XkOu.

0

Skoro już doszliśmy do faktu że to nie (char *) jest przekazywany tylko (char []) to może przyczyną jest to że nie masz odpowiedniego konstruktora i używany jest domyślny (zgaduję).

http://stackoverflow.com/questions/13724242/c11-function-that-only-accepts-string-literals

0

Nie lepiej po prostu napisać normalną implementację i przekleić zaszyfrowanego stringa do kodu? Albo nawet możesz napisać makefile-a czy tam msbuildowy skrypt, żeby robił to za Ciebie

0

Tu już nawet nie chodzi o to, która implementacja lepsza, ale o sam fakt dlaczego tak się dzieje. Ta jest o tyle fajna, że przy każdej kompilacji string jest szyfrowany innym kluczem i że wystarczy jedno makro dla stringów z różnymi długościami.

Dodam jeszcze jeden przykład, który nie działa jak powinien ("1234567890" można przeczytać w notatniku). Przykład identyczny jak poprzedni tyle że nie używa makra ENCRYPT a jawnie wywołuje konstruktor klasy:

#include "EncryptedString.h"
#include <string>
using namespace std;

struct Test
{
    static string Str;
};
string Test::Str = EncryptedString<Indexes<sizeof("1234567890") -1>::Result>("1234567890").Decrypt();

int main()
{
    string str = ENCRYPT("1234567890");
}

Kiedy oszukam trochę z wielkością szyfrowanego stringu to szyfrowanie działa i odbywa się w czasie kompiacji:

#include "EncryptedString.h"
#include <string>
using namespace std;

struct Test
{
    static string Str;
};
string Test::Str = EncryptedString<Indexes<sizeof("1234567890") -2>::Result>("1234567890").Decrypt(); // -2 zamiast -1

int main()
{
    string str = ENCRYPT("1234567890");
}

Czy to może być coś wspólnego z identycznymi szablonami klas?

0

Chciałbym jeszcze dodać dwa przykłady, które ukazują gdzie coś jest nie tak.
Przykład 1.: Szyfrowanie odbywa się w czasie kompilacji, gdyż szyfrowane napisy mają różną długość czyli są innego typu szablonowego:

#include "EncryptedString.h"
#include <string>
using namespace std;
int main()
{
    string str1 = ENCRYPT("123456789");
    string str2 = ENCRYPT("12345");
}

Przykład 2: Szyfrowanie odbywa się w czasie działania programu, napisy mają taką samą długość, jakieś WTF:

#include "EncryptedString.h"
#include <string>
using namespace std;
int main()
{
    string str1 = ENCRYPT("123456789");
    string str2 = ENCRYPT("987654321");
}

Może ktoś na coś wpadnie, czym to może być spowodowane.

0

Nie za bardzo rozumiem co ma tutaj nie działać. Enkoder (konstruktor) na pewno działa w czasie kompilacji, natomiast dekoder działa w run-time. Łańcuch znaków, który przesyłasz do enkodera trafia do dyrektywy .ascii i stąd jego widoczność w binarce. Sprawdziłem ten enkoder pod mingw z g++ w wersji 4.8.1 i że tak powiem "u mnie działa" :]

0

Ponieważ temat jest ciekawy, dopisuję tutaj znalezione rozwiązania dla szyfrowania w czasie kompilacji:

http://www.rohitab.com/discuss/topic/39611-malware-related-compile-time-hacks-with-c11/

http://mradny.blogspot.com/2014/06/c-szyfrowanie-napisow-w-czasie.html

0

@vpiotr podrzucam jeszcze jeden link w tym temacie: https://www.hackinparis.com/node/280

1 użytkowników online, w tym zalogowanych: 0, gości: 1