diff --git a/bazaar/Protect/Protect.cpp b/bazaar/Protect/Protect.cpp index cf39420b3..363088bc6 100644 --- a/bazaar/Protect/Protect.cpp +++ b/bazaar/Protect/Protect.cpp @@ -1,38 +1,47 @@ -#include "Protect.h" - -#ifdef PLATFORM_POSIX -#include -#include - -static bool PROTECT_WRITE_ACCESS(byte *start, size_t size, bool access) -{ - // round start and size to page size -- needed for mprotect - long pSize = sysconf(_SC_PAGESIZE ); - long delta = (long)start % pSize; - if(delta) - { - start -= delta; - size += delta; - } - return mprotect(start, size, access ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_READ | PROT_EXEC) == 0; -} -#else - -static bool PROTECT_WRITE_ACCESS(byte *start, size_t size, bool access) -{ - dword oldProt; - bool res = VirtualProtect(start, size, access ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ, &oldProt); - - return res; -} -#endif - -bool PROTECT_DECRYPT(byte *start, size_t size, String const &key) -{ - RC4 rc4(key); - - if(!PROTECT_WRITE_ACCESS(start, size, true)) - return false; - rc4.Crypt(start, start, size); - return PROTECT_WRITE_ACCESS(start, size, false); -} +#include "Protect.h" + +#ifdef PLATFORM_POSIX +#include +#include + +bool PROTECT_WRITE_ACCESS(byte *start, size_t size, bool access) +{ + // round start and size to page size -- needed for mprotect + long pSize = sysconf(_SC_PAGESIZE ); + long delta = (long)start % pSize; + if(delta) + { + start -= delta; + size += delta; + } + return mprotect(start, size, access ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_READ | PROT_EXEC) == 0; +} +#else + +static bool PROTECT_WRITE_ACCESS(byte *start, size_t size, bool access) +{ + dword oldProt; + bool res = VirtualProtect(start, size, access ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ, &oldProt); + + return res; +} +#endif + +void PROTECT_DECRYPT(byte *start, size_t size, String const &key) +{ + RC4 rc4(key); + + rc4.Crypt(start, start, size); +} + +void PROTECT_OBFUSCATE(byte *start, size_t len, byte *key, size_t keyLen) +{ + String k; + for(unsigned i = 0; i < keyLen; i++) + k += *key++; + if(!PROTECT_WRITE_ACCESS(start, len, true)) + return; + RC4 rc4(k); + rc4.Crypt(start, start, len); + PROTECT_WRITE_ACCESS(start, len, false); +} diff --git a/bazaar/Protect/Protect.h b/bazaar/Protect/Protect.h index f1f3e1efa..77f327585 100644 --- a/bazaar/Protect/Protect.h +++ b/bazaar/Protect/Protect.h @@ -1,118 +1,218 @@ -#ifndef _Protect_h_ -#define _Protect_h_ - -#include -#include "RC4.h" - -using namespace Upp; - -#define PROTECT_START_MARKER "\xba\xad\xde\xad\xfa\xce" -#define PROTECT_END_MARKER "\xc0\xde\xde\xad\xfe\xed" - -#ifdef flagPROTECT - -#ifdef PLATFORM_POSIX - -#define PROTECT_START_FUNC(decrFunc) \ - static bool __decrypted = false; \ - void (*ptr)(void); \ - dword size; \ - if(!__decrypted) \ - { \ - decrFunc((byte *)&&__start, (byte *)&&__end - (byte *)&&__start); \ - __decrypted = true; \ - asm volatile ( \ - "\txor %%eax, %%eax\n" \ - "\tcpuid\n" \ - : \ - : \ - : "eax", "ebx", "ecx", "edx" \ - ); \ - } \ - if(!__decrypted) \ - goto __end; \ - asm volatile( \ - "\tjmp 1f\n" \ - "\t.ascii \""PROTECT_START_MARKER"\"\n" \ - "1:\n" \ - ); \ - __start: \ - { \ - -#define PROTECT_END_FUNC \ - } \ - __end: \ - asm volatile ( \ - "\tjmp 2f\n" \ - "\t.ascii \""PROTECT_END_MARKER"\"\n" \ - "2:\n" \ - ); - -#else - -#define _PROTECT_START_MARKER __asm _emit 0xba __asm _emit 0xad __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfa __asm _emit 0xce -#define _PROTECT_END_MARKER __asm _emit 0xc0 __asm _emit 0xde __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfe __asm _emit 0xed - -#define PROTECT_START_FUNC(decrFunc) \ - static bool __decrypted = false; \ - byte *start, *end; \ - if(!__decrypted) \ - { \ - __asm \ - { \ - __asm lea eax, __start \ - __asm mov start, eax \ - __asm lea eax, __end \ - __asm mov end, eax \ - }; \ - decrFunc(start, end - start); \ - __decrypted = true; \ - __asm \ - { \ - __asm push eax \ - __asm push ebx \ - __asm push ecx \ - __asm push edx \ - __asm xor eax, eax \ - __asm cpuid \ - __asm pop edx \ - __asm pop ecx \ - __asm pop ebx \ - __asm pop eax \ - __asm lea eax, __start \ - __asm mov start, eax \ - __asm lea eax, __end \ - __asm mov end, eax \ - }; \ - } \ - if(!__decrypted) \ - goto __end; \ - __asm { \ - __asm jmp next \ - _PROTECT_START_MARKER \ - __asm next: \ - } \ - __start: \ - { \ - -#define PROTECT_END_FUNC \ - } \ - __end: \ - __asm { \ - __asm jmp next2 \ - _PROTECT_END_MARKER \ - __asm next2: \ - }; - -#endif - -#else - -#define PROTECT_START_FUNC(decrFunc) -#define PROTECT_END_FUNC - -#endif - -bool PROTECT_DECRYPT(byte *start, size_t size, String const &key); - -#endif +#ifndef _Protect_h_ +#define _Protect_h_ + +#include +#include "RC4.h" + +using namespace Upp; + +#define PROTECT_START_MARKER "\xba\xad\xde\xad\xfa\xce" +#define PROTECT_END_MARKER "\xc0\xde\xde\xad\xfe\xed" + +#define OBFUSCATE_START_MARKER "\xba\xad\xde\xad\xfa\xde" +#define OBFUSCATE_END_MARKER "\xc0\xde\xde\xad\xfe\xde" + +#ifdef flagPROTECT + +#ifdef PLATFORM_POSIX + +#define PROTECT_START_FUNC(decrFunc) \ + static bool __decrypted = false; \ + if(!__decrypted) \ + { \ + PROTECT_WRITE_ACCESS((byte *)&&__start, (byte *)&&__end - (byte *)&&__start, true); \ + decrFunc((byte *)&&__start, (byte *)&&__end - (byte *)&&__start); \ + PROTECT_WRITE_ACCESS((byte *)&&__start, (byte *)&&__end - (byte *)&&__start, false); \ + __decrypted = true; \ + asm volatile ( \ + "\txor %%eax, %%eax\n" \ + "\tcpuid\n" \ + : \ + : \ + : "eax", "ebx", "ecx", "edx" \ + ); \ + } \ + if(!__decrypted) \ + goto __end; \ + asm volatile( \ + "\tjmp 1f\n" \ + "\t.ascii \""PROTECT_START_MARKER"\"\n" \ + "1:\n" \ + ); \ + __start: \ + { \ + +#define PROTECT_END_FUNC \ + } \ + __end: \ + asm volatile ( \ + "\tjmp 2f\n" \ + "\t.ascii \""PROTECT_END_MARKER"\"\n" \ + "2:\n" \ + ); + +#define OBFUSCATE_START_FUNC \ + PROTECT_OBFUSCATE((byte *)&&__start, (byte *)&&__end - (byte *)&&__start, (byte *)&&__init + 2, 16 /* sizeof(OBFUSCATE_START_MARKER) + sizeof("0123456789") */); \ + asm volatile ( \ + "\txor %%eax, %%eax\n" \ + "\tcpuid\n" \ + : \ + : \ + : "eax", "ebx", "ecx", "edx" \ + ); \ + __init: \ + asm volatile( \ + "\tjmp 1f\n" \ + "\t.ascii \""OBFUSCATE_START_MARKER"\"\n" \ + "\t.ascii \"0123456789\"\n" \ + "1:\n" \ + ); \ + __start: + +#define OBFUSCATE_END_FUNC \ + asm volatile( \ + "\tjmp 1f\n" \ + "\t.ascii \""OBFUSCATE_END_MARKER"\"\n" \ + "1:\n" \ + ); \ + __end: \ + PROTECT_OBFUSCATE((byte *)&&__start, (byte *)&&__end - (byte *)&&__start, (byte *)&&__init + 2, 16 /* sizeof(OBFUSCATE_START_MARKER) + sizeof("0123456789") */) + +#else + +#define _PROTECT_START_MARKER __asm _emit 0xba __asm _emit 0xad __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfa __asm _emit 0xce +#define _PROTECT_END_MARKER __asm _emit 0xc0 __asm _emit 0xde __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfe __asm _emit 0xed + +#define _OBFUSCATE_START_MARKER __asm _emit 0xba __asm _emit 0xad __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfa __asm _emit 0xde +#define _OBFUSCATE_END_MARKER __asm _emit 0xc0 __asm _emit 0xde __asm _emit 0xde __asm _emit 0xad __asm _emit 0xfe __asm _emit 0xde +#define _OBFUSCATE_KEYPLACER __asm _emit 0x00 __asm _emit 0x01 __asm _emit 0x02 __asm _emit 0x03 __asm _emit 0x04 __asm _emit 0x05 __asm _emit 0x06 __asm _emit 0x07 __asm _emit 0x08 __asm _emit 0x09 + +#define PROTECT_START_FUNC(decrFunc) \ + static bool __decrypted = false; \ + byte *__startPtr, *__endPtr; \ + if(!__decrypted) \ + { \ + __asm \ + { \ + __asm push eax \ + __asm lea eax, __start \ + __asm mov __startPtr, eax \ + __asm lea eax, __end \ + __asm mov __endPtr, eax \ + __asm pop eax \ + }; \ + PROTECT_WRITE_ACCESS(__startPtr, __endPtr - __startPtr, true); \ + decrFunc(__startPtr, __endPtr - __startPtr); \ + PROTECT_WRITE_ACCESS(__startPtr, __endPtr - __startPtr, false); \ + __decrypted = true; \ + __asm \ + { \ + __asm push eax \ + __asm push ebx \ + __asm push ecx \ + __asm push edx \ + __asm xor eax, eax \ + __asm cpuid \ + __asm pop edx \ + __asm pop ecx \ + __asm pop ebx \ + __asm pop eax \ + }; \ + } \ + if(!__decrypted) \ + goto __end; \ + __asm { \ + __asm jmp __next \ + _PROTECT_START_MARKER \ + __asm __next: \ + } \ + __start: \ + { \ + +#define PROTECT_END_FUNC \ + } \ + __end: \ + __asm { \ + __asm jmp __next2 \ + _PROTECT_END_MARKER \ + __asm __next2: \ + }; + +#define OBFUSCATE_START_FUNC \ + byte *__startPtr, *__endPtr, *__keyPtr; \ + __asm \ + { \ + __asm push eax \ + __asm lea eax, __start \ + __asm mov __startPtr, eax \ + __asm lea eax, __end \ + __asm mov __endPtr, eax \ + __asm lea eax, __init \ + __asm inc eax \ + __asm inc eax \ + __asm mov __keyPtr, eax \ + __asm pop eax \ + }; \ + PROTECT_OBFUSCATE(__startPtr, __endPtr - __startPtr, __keyPtr, 16); \ + __asm \ + { \ + __asm push eax \ + __asm push ebx \ + __asm push ecx \ + __asm push edx \ + __asm xor eax, eax \ + __asm cpuid \ + __asm pop edx \ + __asm pop ecx \ + __asm pop ebx \ + __asm pop eax \ + }; \ + __init: \ + __asm { \ + __asm jmp __next \ + _OBFUSCATE_START_MARKER \ + _OBFUSCATE_KEYPLACER \ + __asm __next: \ + } \ + __start: \ + +#define OBFUSCATE_END_FUNC \ + __asm { \ + __asm jmp __next2 \ + _OBFUSCATE_END_MARKER \ + __asm __next2: \ + }; \ + __end: \ + PROTECT_OBFUSCATE(__startPtr, __endPtr - __startPtr, __keyPtr, 16) + + +#endif + +// Check macro for program startup -- executes next statement if invalid key +#define ON_PROTECT_BAD_KEY(decrFunc) \ + bool __keyOk; \ + { \ + const char *crypted = PROTECT_START_MARKER "abracadabra" PROTECT_END_MARKER; \ + const int len = strlen("abracadabra"); \ + Bufferbuf(len); \ + memcpy(buf, crypted + 6 /* sizeof(PROTECT_START_MARKER)*/, len); \ + decrFunc(buf, len); \ + __keyOk = !memcmp(buf, "abracadabra", len); \ + } \ + if(!__keyOk) + +#else + +#define PROTECT_START_FUNC(decrFunc) +#define PROTECT_END_FUNC +#define OBFUSCATE_START_FUNC +#define OBFUSCATE_END_FUNC +#define ON_PROTECT_BAD_KEY(decrFunc) if(false) + +#endif + +bool PROTECT_WRITE_ACCESS(byte *start, size_t size, bool access); +void PROTECT_DECRYPT(byte *start, size_t size, String const &key); +void PROTECT_OBFUSCATE(byte *start, size_t len, byte *key, size_t keyLen); + +#endif diff --git a/bazaar/Protect/RC4.cpp b/bazaar/Protect/RC4.cpp index 81d7a8a0f..30d7dc3aa 100644 --- a/bazaar/Protect/RC4.cpp +++ b/bazaar/Protect/RC4.cpp @@ -49,3 +49,22 @@ void RC4::Crypt(byte *src, byte *dst, size_t len) *dst++ = *src++ ^ sbox[swap]; } } + +String RC4::Crypt(String const &s) +{ + unsigned char swap; + int i = 0; + int len = s.GetCount(); + String res; + + while (len--) + { + sj += sbox[++si]; + swap = sbox[si]; + sbox[si] = sbox[sj]; + sbox[sj] = swap; + swap = sbox[si] + sbox[sj]; + res += s[i++] ^ sbox[swap]; + } + return res; +} diff --git a/bazaar/Protect/RC4.h b/bazaar/Protect/RC4.h index 862dc801a..8d6ec0473 100644 --- a/bazaar/Protect/RC4.h +++ b/bazaar/Protect/RC4.h @@ -18,6 +18,7 @@ class RC4 RC4(String const &key); void SetKey(String const &key); void Crypt(byte *src, byte *dst, size_t len); + String Crypt(String const &s); }; #endif diff --git a/bazaar/Protect/srcdoc.tpp/HowItWorks$en-us.tpp b/bazaar/Protect/srcdoc.tpp/HowItWorks$en-us.tpp index c6a7ddb75..eefa87a67 100644 --- a/bazaar/Protect/srcdoc.tpp/HowItWorks$en-us.tpp +++ b/bazaar/Protect/srcdoc.tpp/HowItWorks$en-us.tpp @@ -941,10 +941,54 @@ FF`" );]&] file, from internet or generated by a combination of machine identification number and a license key.&] [s0; &] +[s0; You can also check for key correctness on app startup, and abort +the app if key is incorrect; the macro [* ON`_PROTECT`_BAD`_KEY(decrFunc)] +executes a block of code only if the key isn`'t correct. Here +an example usage :&] +[s0; &] +[s0;l128; [*/ ON`_PROTECT`_BAD`_KEY(decrFunc)]&] +[s0;l128; [/ `{]&] +[s0;l128; [/ -|PromptOK(`"License error`");]&] +[s0;l128; [/ -|exit(1);]&] +[s0;l128; [/ `}]&] +[s0;l128; &] [s0; That`'s all ! If you build your application with TheIde, selecting the [* GUI PROTECT ]main configuration, it`'ll be automatically built and encrypted with the key you`'ve chosen.&] [s0; You can see all that working in ProtectTest demo application; if you change the key in its [* GetKey()] function your app will -crash upon run; if the key is correct, it`'ll run normally.&] -[s0; ] \ No newline at end of file +signal the license error upon run, giving the user the choice +of exiting the app or continue anyways. Of course, if you choose +to continue, the app will crash.&] +[s0; If the key is correct, no alert is shown and the app runs normally.&] +[s0; &] +[s0; A note : the macros [* PROTECT`_START`_FUNC()] and [* PROTECT`_END`_FUNC] +creates an internal block of code enclosed by a couple of `{`}, +for technical reasons. The caveat is that [*/ any][* ]variable defined +[*/ inside ]the macro couple will not be visible from the outside. +So :&] +[s0; &] +[s0;l160; [/ -|][*/ PROTECT`_START`_FUNC(Decrypt);]&] +[s0;l160; [/ -|int i `= 10;]&] +[s0;l160; [/ -|][*/ PROTECT`_END`_FUNC;]&] +[s0;l160; [*/ -|][/ return i;]&] +[s0; &] +[s0; Will give an error because of undefined variable i; the solution +is to put variable definition [/ before ][* PROTECT`_START`_FUNC] +or the return statement [/ inside ]the macro couple :&] +[s0; &] +[s0; &] +[s0;l160; [/ -|int i `= 10;]&] +[s0;l160; [/ -|][*/ PROTECT`_START`_FUNC(Decrypt);]&] +[s0;l160; [/ -|][*/ PROTECT`_END`_FUNC;]&] +[s0;l160; [*/ -|][/ return i;]&] +[s0; &] +[s0; or&] +[s0; &] +[s0;l160; [/ -|][*/ PROTECT`_START`_FUNC(Decrypt);]&] +[s0;l160; [/ -|int i `= 10;]&] +[s0;l160; [*/ -|][/ return i;]&] +[s0;l160; [/ -|][*/ PROTECT`_END`_FUNC;]&] +[s0; &] +[s0; On next chapter we`'ll introduce another kind of copy protection +tool named [^topic`:`/`/Protect`/srcdoc`/Obfuscation`$en`-us^ obfuscation].] \ No newline at end of file diff --git a/bazaar/Protect/srcdoc.tpp/Obfuscation$en-us.tpp b/bazaar/Protect/srcdoc.tpp/Obfuscation$en-us.tpp new file mode 100644 index 000000000..bdd7a2571 --- /dev/null +++ b/bazaar/Protect/srcdoc.tpp/Obfuscation$en-us.tpp @@ -0,0 +1,81 @@ +topic "Code Obfuscation"; +[ $$0,0#00000000000000000000000000000000:Default] +[{_}%EN-US +[s0;%- [*R6 Code Obfuscation]&] +[s0;/%- &] +[s0; As you can easily understand, the [^topic`:`/`/Protect`/srcdoc`/HowItWorks`$en`-us^ e +ncryption method proposed earlier] has 2 flaws :&] +[s0; &] +[s0;i150;O0; The decrypting code is visible and traceable&] +[s0; &] +[s0;i150;O0; You must obtain the key [*/ before ]the decryption phase&] +[s0; &] +[s0; The first point is not so important: even if that`'s true that +user can step the decryptor and get the real code, he must do +it for every function you did protect, record the code, repatch +the executable in many places (they can be hundreds, if needed), +disable the decryption parts, and so on.... an huge work, often +not worth the time spent. And, of course, if your app is so valuable +you can resort to a commercial tool !&] +[s0; &] +[s0; Second point is somehow more sensitive : if the cracker gets +the key, he can write a tool that automatically decrypts and +patch your application in short time, even if it contains hundred +of protected functions. Is not so easy, but can be done in a +reasonable time.&] +[s0; A perfect solution for that doesn`'t exist... of course. But, +we can make cracker`'s live a bit more difficult if we encrypt +somehow also sensitive parts of code with a random key.&] +[s0; This is done by another couple of macros :&] +[s0; &] +[s0;l160; [/ void MyObfuscatedFunction(void)]&] +[s0;l160; [/ `{]&] +[s0;l160; [/ -|][*/ OBFUSCATE`_START`_FUNC;]&] +[s0;l160;/ &] +[s0;l160; [/ -|]&] +[s0;l160;/ &] +[s0;l160; [/ -|][*/ OBFUSCATE`_END`_FUNC;]&] +[s0;l160;/ &] +[s0;l160; [/ `}]&] +[s0; &] +[s0; You can notice that the [* decrFunc ]parameter is gone. The macro +generates a random, 16 byte long key, different for each obfuscated +function. The key is stored into the executable and automatically +used to decrypt the code on the fly when needed [*/ and ]to reencrypt +it at function exit.&] +[s0; The code will run anyways, it won`'t check any key, so what`'s +the purpose ?&] +[s0; Is simply to defeat disassemblers and to make function tracing +quite hard.&] +[s0; The disassembler would see just garbage bytes, as the function +is stored in encrypted form. If you try to trace it step by step +with a debugger, the function will be unencrypted just when needed, +and re`-encrypted on exit, which confuses many debuggers (TheIde +GDB frontend is `*very`* confused about it :`-) )&] +[s0; You`'d normally use [* Obfuscation ]when obtaining your key used +to decrypt the [* Protected ]parts, mostly on code startup and +on some sensitive parts which needs to be executed [/ before ]the +key is available.&] +[s0; &] +[s0; A note : as opposite as [* PROTECT`_xxx`_FUNC()] macro block, the +[* OBFUSCATE`_xxx`_FUNC] one [/ doesn`'t] create an enclosed code +block, so the visibility of variable is [/ global ]to function; +more important, the code [*/ MUST ]reach the end macro in order +to [*/ re`-obfuscate] itself. That means that you [*/ can`'t] put +[*/ any] return statement inside the obfuscation block. If you +do, your app will crash if the obfuscated function is executed +twice. So :&] +[s0; &] +[s0;l160; [/ -|][*/ OBFUSCATE`_START`_FUNC;]&] +[s0;l160; [*/ -|][/ ]&] +[s0;l160; [/ -|return;]&] +[s0;l160; [/ -|][*/ OBFUSCATE`_END`_FUNC;]&] +[s0; &] +[s0; Is [*/ WRONG ]and will make your app crash on second function +invocation; the right approach is :&] +[s0; &] +[s0;l160; [/ -|][*/ OBFUSCATE`_START`_FUNC;]&] +[s0;l160; [*/ -|][/ ]&] +[s0;l160; [/ -|][*/ OBFUSCATE`_END`_FUNC;]&] +[s0;l160; [/ -|return;]&] +[s0; ] \ No newline at end of file