Pull to refresh

Comments 24

А каким обфускатором программа была запакована, с какими настройками?


Если просто открыть программу в .net Reflector, мы увидим следующее:

Есть dnSpy — он тоже позволяет менять код, но еще бесплатный и опенсорсный.


Нужно найти в коде строку How did you got that?! и идущий перед ней опкод изменить с brtrue.s на brfalse.s

В теории можно и этому помешать: проверять хеш-код всей программы или даже отдельных методов. Это, конечно, тоже можно взломать, но потребуется больше времени (но в этом и смысл обфускации — затратить как можно больше времени у взломщика).

Обфускатор .net Recator.
За dnSpy спасибо, посмотрю что за зверёк.
По поводу проверки хеша программы, да, но он не спасет если на руках алгоритм проверки пароля.

А как вы бы предложили реализовать такую проверку? Я сейчас так и делаю, но приходится компилировать защищаемый файл многократно чтобы в контрольной сумме файла появилась нужная комбинация чисел.

Хранить контрольную сумму файла не в самом ехе файле, а в другой библиотеке. Конрольную сумму библиотеки в другой библиотеке. И так далее. И проверку делать не сразу при запуске программы, а через некоторое время. Чем больше времени от запуска до срабатывания произойдёт - тем лучше. И проверять не все конрольные суммы разом. В разных местах вызовы проверки сумм из разных библиотек выполнять. И это должна быть не одна и та же функция, а для каждой проверки отдельная фукнция, чтобы каждую отдельно взламывать пришлось. Даже одну и ту же проверку можно делать не каждый раз, а с какой-то вероятностью или только после нескольких включений программы, чтобы радостный хакер опубликовал недоломанную сборку. Ну и т.д и т.п.

В теории можно и этому помешать: проверять хеш-код всей программы или даже отдельных методов
В этой проверке точно так же меняется опкод условия. Ниже есть комментарий как усложнить такой взлом, но в общем случае — гонка щита и меча.
UFO just landed and posted this here

Если не отлаживать пошагово, то сишноподобный код так же хорошо понятен, потому что стандарные методы декомпилятор за тебя определил и обозвал. И ты погружен только в логику что написал сам программист.

Со времён SoftICE появился HexRays в IDA, и он по сишному бинарнику восстанавливает более-менее понятный сишный код.

С другой стороны, в c# можно так же навернуть виртуальную машину с самомодифицирующимся байт-кодом, и разобраться будет очень сложно, придётся начинать с написания инструментария.
Писать крэкми на с# довольно бестолково, как раз потому что получить исходник на довольно высоком уровне (а не в ассемблерных коммандах) раз плюнуть.

Может крэкми и бесполезно, но в целом обфускация имеет смысл, особенно если нужно извлечь логику из программы, а не просто взломать ее. Злоумышленнику нужно убирать всяческие защиты и восстанавливать идентификаторы. Последнее придется делать заново для каждой следующей версии, т.к. идентификаторы меняются рандомно и это происходит с потерей информации.

Ну а имея исходник зареверсить логику тривиальная задача.
Но только не в случае C#, где у вас без отладчика попросту нет способа определить, при каких условиях вызовется тот или иной кусок кода.
Потому что динамическое связывание, потому что неявные вызовы, потому что код активируется по событию внутри события внутри события.
Добавьте многопоточность, и в этой каше сам чёрт ногу сломит. Вся логика приложения нарезана на мелкие кусочки, каждый из которых вы конечно можете отреверсить, но собрать из них цельный паззл на одном только декомпиляторе у вас не выйдет — нет явно прописанных условий перехода от одного фрагмента к другому.
ИМХО, даже сильно обфусцированные C++ приложения (например, замусоренные чем-то вроде ExeCryptor) изучать легче, чем эту невнятную кашу. Восстановить логику работы чего-то сложнее Hello World можно только в отладчике, да и то усилий нужно приложить очень много.

а крэклаб (exelab) еще жив? раньше там море таких статей было и обсуждения интересные

Автор , если вы раскопали логику правил проверки, то логичнее было бы подбирать числа не перебором всех, а подбирать конкретные цифры.

Смотрим на последнее правило (разница в 4) - пары простые 04, 15, 26, 37, 48, 59 Для каждой из этих пар определена шестая цифра, значит легко подобрать все пары к ним для второго правила (перебираем третью цифру, вычисляем вторую).

Так за ~60 итераций мы узнали все возможные цифры на четырех позициях. Третье правило дает, что 4 и 8 цифра не должны быть одновременно нечетными. Проверим 100 комбинаций, получим 75 пар.

За 60*75 итераций мы получаем все цифры на шести позициях. Для каждой добавляем еще проверку перебором пятой позиции (она в правилах не фигурирует) и первой, проверяя делимость. Итого ~450000 итераций против ~100000000, а это два порядка.

Спасибо, интересно, учту.

Если стоит задача оптимизировать, то лучше от строк избавиться.
Чтобы получить n-ую цифру в числе, не обязательно конвертировать его в строку, вырезать цифру и конвертировать обратно в число.

Нет, конкретно такой задачи не стояло.
Но да, можно через процент от деления нужный разряд выбрать.

Уже голова перестала сооражать, остаток от деления

   private static void smethod_0(int x, ParallelLoopState pls)
        {
            int num = x;

            int x1 = num / 10000000;
            if (num % x1 != 0) return;
            num = num % 10000000;
            int x2 = num / 1000000;
            num = num % 1000000;
            int x3 = num / 100000;
            num = num % 100000;
            int x4 = num / 10000;
            num = num % 10000;
            int x5 = num / 1000;
            num = num % 1000;
            int x6 = num / 100;
            if (x3 + x6 != x2) return;
            num = num % 100;
            int x7 = num / 10;
            if (x7 - x6 != nfmV) return;
            num = num % 10;
            int x8 = num;
            if (x4 * x8 % 2 != 0) return;

            allPwd += $"{x}\n";
        }

Убрал строки, ну и проверки вывел в порядки их возможного использования.
Скорость выполнения увеличилась с 13 секунд, до 3.5 сек.
Спасибо за идею.

Вместо паттерна


int a = b % x;
int c = d / x;

Рекомендую использовать


int c = Math.DivRem(b, x, out a);

Это быстрей работает (через умножение и вычитание, а в будущем вообще будет через одну операцию), и семантически ясней выглядит. А будет еще наглядней, когда версия с туплами подъедет (может уже есть в net 6):


(a, c) = Math.DivRem(b, x);

Что интересно, время выполнения выросло на 0.6 секунды в среднем, с 3.5 до 4.1, при использовании DivRem
.net используется 4.7.1

Это странно — возможно проблема в чем-то другом. А на коровских версиях как?

На Core 5, быстрее в два раза.
За 2.2 сек.
Определенно прогресс по сравнению .net 4 есть

Ой, ошибся — в первом фрагменте должно быть:


int a = b % x;
int c = b / x;

"И я намеренно упускаю, первые 10 миллионов паролей от 00000000 до 09999999" - все равно на 0 делить нельзя, первое условие.

Да, вы правы. Моя невнимательность сыграла

Sign up to leave a comment.

Articles