Взломать возможно все, даже очень защищенную программу. Ниже я попробовал привести простые и краткие примеры, которые усложнят взлом Вашего кода, или даже помогут избежать взлома.
1 Сравнения паролей
Ни когда не хранить пароль в открытом виде в коде, а так же не сравнивать в веденое значение с тем что храним… Тут даже навыков не надо, что бы это прочитать.
Если уж логика программы такова что приходится хранить пароль в коде, то храните его уже в MD5…В случае если строку прочтут, то не смогут восстановить по ней пароль, а собственно и ввести его:
C++ | 1
2
3
4
5
| //Так делать нельзя
if(Edit1->Text=="Очень сложный пароль")
{
//Активация прошла
} |
|
C++ | 1
2
3
4
5
| //Делать нужно так
if(MD5(Edit1->Text)=="BC3EFCAB952E094CB11BF964ACFDC23D") //Тут мы возводим введенный пароль в MD5 и сравниваем с уже хранимым значением в MD5
{
//Активация прошла
} |
|
А еще луче совместить данный способ с 5 пунктом
2 Прерывание процедур
Старайтесь вообще отказаться при сравнивании паролей или регистраций от ShowMessage или других модальных сообщений, которые делают прерывание процедуры, тем самым дают знать, где Вы делаете проверку.
Как вариант держать отдельную форму для таких уведомлений. Бонусом будет если до проверки и после проверки будет добавлен какой то код, что усложнит поиски.
C++ | 1
2
3
4
5
| //Так делать нельзя
if(MD5(Edit1->Text)!="BC3EFCAB952E094CB11BF964ACFDC23D")
{
ShowMessage("Пароль введен не верно");
} |
|
C++ | 1
2
3
4
5
6
7
8
9
10
11
| //Как вариант делать так
if(MD5(Edit1->Text)!="BC3EFCAB952E094CB11BF964ACFDC23D")
{
// какой то код
//показываем форму с надписью о неверном пароле
TForm * f = new TForm (Application);
f->Show();
// какой то код
} |
|
3 Возращения значений функции
Не делайте отдельные функции для авторизаций или хотя бы делайте их частью чего то жизни - важного для работы программы.
Для взломщика не чего не стоит подменить возвращающее значение, при этом даже ему ненужно знать пароль…тут и ломать не чего.
К примеру можно создать сам TSocket для общения с сервером при положительном вводе пароля. Это очень хороший прием, так как взломщик не сможет простыми манипуляциями этого добиться, и ему как вариант придется писать дополнительную dll.
На будущее помните, что подменить возвращаемое значение из функции не так уж и сложно, а так же параметры, посылаемые в функции.
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| //Ниже функция авторизации возвращает результат как булевое значение
//Так делать нельзя
bool __fastcall TForm1::try_pass()
{
if(MD5(Edit1->Text)=="BC3EFCAB952E094CB11BF964ACFDC23D")
return true;
else
return false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (try_pass()==true)
{
//Авторизация выполнена
}
}
//--------------------------------------------------------------------------- |
|
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| //как луче сделать
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// какой то код
if(MD5(Edit1->Text)=="BC3EFCAB952E094CB11BF964ACFDC23D")
{
// какой то код
//Авторизация выполнена
//создаем для примера жизненно-важную кнопку, без которой программа будет не полноценной
TButton * b = new TButton (this);
b->OnClick = ...
// какой то код
}
// какой то код
}
//--------------------------------------------------------------------------- |
|
4 Статичные переменные и глобальные переменные
Глобальные переменные очень легко найти и подменить их значение. Как и другие константы переменных, они всегда находится в определённом месте приложений. Злоумышленнику стоит только раз определить местоположение и поставить на нем «Point»…и по прицепу программ как «ArtMoney» или «O`matic»…значение данных переменных будет всегда изменено.
В критически важных секциях приложения, луче использовать динамически создаваемые переменные. Каждый раз при создании такой переменной, она будет писать свои значения в новую область памяти, тем самым усложнив поиск.
Как пример, можно сделать так:
C++ | 1
2
3
4
5
6
| // было
int i =10;
// Стало
int *i = new int[1];
i[0] = 10; |
|
Бонусом для данного типа данных можно сделать пересоздания переменой во время работы приложения:
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| //Объявляем глобально
int *i= new int[1];
//Где то присваиваем ей значение
i[0] = 10;
/*Ниже код пересоздает перемееную*/
//создаем перемеренную провайдера, что бы сохранить данные
int *j = new int[1];
j[0] = i[0];
//удаляем и персоздаем заново, но уже с другим адресом
delete [] i;
i= new int[1];
i[0] = j[0];
delete [] j; |
|
5 Контейнеры для хранения данных
Хорошим ходом для защиты данных и их вскрытия, может послужить создания контейнера, в котором данные хранятся в зашифрованном виде.
Принцип такой:- Создается контейнер
- Есть функция, которая предварительно шифрует параметры и кладет их в контейнер
- Другая функция по ключу вынимает данные и расшифровывает.
Данный метод сводит к минимум возможность нахождения и вскрытия данных. Ниже приведен простейший пример реализации на основе TStringList.
C++ | 1
2
| //Глобально объявляем наш простенький контейнер
TStringList * tajna = new TStringList; |
|
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
| //---------------------------------------------------------------------------
void __fastcall TForm1::AddString(AnsiString st)
{
//Функция предварительно шифрует и добавляет строку в контейнер
double z;
for (int i=1;i<=st.Length();i++)
{
z=i;
char mask=sin(z)*400-sin(z)*250/20;
st[z]=(st[z] ^ mask);
}
tajna->Add(st);
}
//--------------------------------------------------------------------------- |
|
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| AnsiString __fastcall TForm1::GetString(int id)
{
//функция выбирает id строки, дешифрует и возвращает
AnsiString st = tajna->Strings[id];
double z;
for (int i=1;i<=st.Length();i++)
{
z=i;
char mask=sin(z)*400-sin(z)*250/20;
st[z]=(st[z] ^ mask);
}
return st;
}
//--------------------------------------------------------------------------- |
|
6 Защита исполняемого файла
Для того что бы защитить свое приложение от модификаций (не путаем с запущенным приложением), мы можем в конец файла дописывать его хеш значение, а после из тела программы проверить на модификацию.
В случае если при сравнении значения будут разница, мы можем понять был ли исходный файл модифицирован и предпринять какие то меры.
Надо помнить, что при модификации самого файла, злоумышленник может и вписать новое значения хеш. Что бы этого не случилось, в получения хеш значения мы добавляем «Соль». Ниже пример кода для приложения что дописывает хеш в конец файла, а так же код для сверки непосредственно в само приложение.
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| //Функия для приложения которое будет дописывать в конец строки его хеш значение
#include <IdHashMessageDigest.hpp>
if(OpenDialog1->Execute())
{
//выбираем файл и получем его Хеш + "СОЛЬ"
TIdHashMessageDigest5 *MD5 = new TIdHashMessageDigest5();
TMemoryStream * F = new TMemoryStream;
TByteDynArray Key;
F->LoadFromFile(OpenDialog1->FileName);
F->Position = 0;
Key = MD5->HashStringAsHex(MD5->HashStreamAsHex(F)+"Соль").BytesOf();
//Дописываем это значение в конец файла (значение можно и зашифровать, тут как фантазия ляжет)
F->WriteBuffer(&Key[0],Key.Length);
//подменяем наше приложение на с проверочной строкой
F->SaveToFile(OpenDialog1->FileName);
//Очищаем данные
delete F,MD5;
} |
|
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| //Функция "самозащиты", которая проверяет запущенное приложение на модернизацию
//#include <IdHashMessageDigest.hpp>
//выбираем файл
TIdHashMessageDigest5 *MD5 = new TIdHashMessageDigest5();
TMemoryStream * F = new TMemoryStream;
TMemoryStream * F1 = new TMemoryStream;
TByteDynArray Key;
F->LoadFromFile(Application->ExeName);
F->Position = 0;
//Отделяем проверочную строкуот корня файла и получаем хеш и делаем сравнение
F1->CopyFrom(F,F->Size-32);
F1->Position = 0;
F->Position =F1->Size;
Key.set_length(32);
F->ReadBuffer(&Key[0],32);
if(MD5->HashStringAsHex(MD5->HashStreamAsHex(F1)+"Соль")!=TEncoding::UTF8->GetString(Key))
{
//Данные не сошлись, завершаем экстренно приложение
ExitProcess(0);
}
//Очищаем данные
delete F,F1,MD5;
Key.set_length(0); |
|
Добавлю что метод сверки луче помещать в тело других функций, но ни как не в событие создания формы, дабы его просто напросто не отключили.
Минусом данной реализации является дальнейшая подпись приложения электронно-цифровой подписью (если конечно Вы ей пользуетесь).
Заключение
Примеры что приведены выше являются элементарными и их модернизация ограничена только Вашей фантазий, хоть для кого то они и покажутся спорными.
Добавлю от себя, если планируете проект в массы, не поленитесь и задумайтесь об его защите. |