Bazaar/Protect : added Obfuscation, key check and updated docs

git-svn-id: svn://ultimatepp.org/upp/trunk@2717 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
micio 2010-09-21 22:40:59 +00:00
parent 7eb3190381
commit 8a462f4233
6 changed files with 412 additions and 158 deletions

View file

@ -1,38 +1,47 @@
#include "Protect.h"
#ifdef PLATFORM_POSIX
#include <sys/mman.h>
#include <unistd.h>
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 <sys/mman.h>
#include <unistd.h>
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);
}

View file

@ -1,118 +1,218 @@
#ifndef _Protect_h_
#define _Protect_h_
#include <Core/Core.h>
#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 <Core/Core.h>
#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"); \
Buffer<byte>buf(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

View file

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

View file

@ -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

View file

@ -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; ]
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].]

View file

@ -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; [/ -|<here my function code>]&]
[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; [*/ -|][/ <some code here>]&]
[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; [*/ -|][/ <some code here>]&]
[s0;l160; [/ -|][*/ OBFUSCATE`_END`_FUNC;]&]
[s0;l160; [/ -|return;]&]
[s0; ]