Случайно в сети заметил один примерчик который вынес мозг, а создание дефайнов для операторов языка. Както ранее не думал об этом. Решил по экспериментировать и задифайнить всё что возможно, а не только имена системных операций типа for if else Вот пример: прогармка вида: Code: int main() { int x; int z = 0; for (x = 0; x < 20; x++) { if (x % 3 < 2) { z = z + x; } else { z = z * x; } } printf("%i", z); return 3; } которая выводит на экран число 1271008 может после обфускации выглядеть так: Code: #define a3 else #define a21 3 #define a4 < #define a5 ; #define a6 = #define a23 main #define a18 + #define a2 for #define a19 * #define a20 2 #define a7 ++ #define a14 ) #define a8 int #define a10 % #define a1 if #define a11 { #define a17 , #define a12 } #define a3 else #define a13 ( #define a22 20 #define a15 printf #define a24 return #define a16 "%i" #define a25 0 a8 a23 a13 a14 a11 a8 a0 a5 a8 a9 a6 0 a5 a2 a13 a0 a6 a25 a5 a0 a4 a22 a5 a0 a7 a14 a11 a1 a13 a0 a10 a21 a4 a20 a14 a11 a9 a6 a9 a18 a0 a5 a12 a3 a11 a9 a6 a9 a19 a0 a5 a12 a12 a5 a15 a13 a16 a17 a9 a14 a5 a24 a21 a5 a12 В частности это пригодиться когда необходимо чтобы программа компилировалась, но при этом исходный код был слегка в испорченном виде, типа чтобы банального копипаста не было. В примере просто юзались все имена вида aN, а на деле можно использовать что угодно, чтобы внести путаницу, к томуже можно использовать кучу фейковых дефайнов которые не будут вызываться но будут запутывать код.
Вообще реальная обфускация получится если все #define разместить в одном модуле, а непосредственно код - в другом. Тогда получится что файл с кодом будет состоять тока из текста вида: Code: #include "def.h" a8 a23 a13 a14 a11 a8 a0 a5 a8 a9 a6 0 a5 a2 a13 a0 a6 a25 a5 a0 a4 a22 a5 a0 a7 a14 a11 a1 a13 a0 a10 a21 a4 a20 a14 a11 a9 a6 a9 a18 a0 a5 a12 a3 a11 a9 a6 a9 a19 a0 a5 a12 a12 a5 a15 a13 a16 a17 a9 a14 a5 a24 a21 a5 a12 Прям аналог брейнфака получается)
Несколько вопросов: 1) Как проводилась обфускация, через какую прожку? 2) На сколько увеличился размер исполняемого файла? 3) Так можно любые конструкции изменить? или это дело зависимо? p.s. Сорри, возможно, вопросы глупые, но интересно узнать
1) провелась обфускация - ручками. нет ничего сложного 2) размер исполняемого файла вообще не должен менять потому что при компиляции компилятор сначало подставляет дефайны а потом уже начинает компилить код, так что считай код восстанавливается полностью до того состояния которое и было. 3) 99,99% конструкций поддаются такому. единственное не поддаётся обфускации это #include #define и им подобные которые начинаются на #, а так всё остальное запросто. Ну и конечно текстовые строки тяжелее будет обработать если вообще возможно будет, а так и типы данных и переменные и операции с переменными и числа и имена функций и процедуры и системные имена - всё можно изменить Кстати, деобфусковать именно эту прогу достаточно просто - строк 50 кода на php. Это как пример просто, а на деле можно написать такое что тяжко будет что-то делать
Ну а как быть тогда, если потребуется "закодить" таким образом полноценную, более сложную программу? Разве что отдельные участки... На весь код потребуется куча времени, раз все делается руками. Можешь привести пример, где тебе это пригодится? (провести обфускацию отдельных только участков кода)
Метод основан на банальнейшей подстановке, то есть А заменяется на Б и все, как быть? Ну например есть такая функция в средах разработки: найти и заменить вот так и быть Где пригодится? Ну например выложить исходник в паблик, но чтоб ламеры не особо веселились... этот метод просто ухудшает читабельность кода, но не влияет на его работоспособность...
Чисто теоретически чтобы написать прогу которая сама будет такое тварить - тут тоже не составит труда. просто парочка правил должна быть. Но сложность в том, что парсить строки чуть сложнее чем код. т.е. стркои вида "xxx" "xx\"x" "xx\\"x\\" всё что угодно может быть. А для всего другова надо чтобы код был написан подходящим образом т.е. пробелы между каждой операций и прееменными типа for ( x = 1 ; x < 23 ; x += 1 ) { zzzzz ; } тогда парсить легко будет иначе надо вольно долго угукаться
Ну, это если совсем от новичков защищать. Но им и исходный код не нужен будет. Думаю, кто разбирается в чужом коде, сможет написать скрипт обратной замены.
ну не всё так просто. потому что можно юзать вложенные дефайны. типа #define a1 for #define a2 a1 #define a3 a2 #define a4 a3 итд пока не надоест. так что ты будешь пару дней писать тока парсер всё этого
Те-же яйца, только в профиль. Те-же яйца, только в профиль. Собрав оба ниже приведённых листинга воедино и откомпилировав файл, мы получаем вполне работоспособную программу, запрашивающую число и выводящую его в обратном порядке. Как видно, трансляция сишного кода в стихи осуществлялась тривиальной контекстной заменой, причем, часто употребляемые слова английского языка (and, a, the) определены через пробел. Короче, идея понятна. Проблема в том, что практически все компиляторы позволяют вывести результат работы препроцессора в файл (у MS VC за это отвечает ключ /P), проанализировать который не составит никакого труда. Чтобы трахнуть мозги (мы же хотим трахнуться, правда?), необходимо использовать более продвинутые методики. Например, хитрые математические алгоритмы (типа сходящихся рядов), реализованные в виде развернутых циклов. Заслуга препроцессора в том, что он позволяет представить эти ряды в псевдографической форме, высаживающей хакеров на реальную измену. Дефайны: Code: #define Twas int #define the #define night main() #define before { #define Christmas int number, rightDigit, sign = 0; #define And #define all printf("Enter your number: "); #define through scanf("%d", &number); #define house if (number < 0) #define Not #define a #define creature { #define was number = -number; #define stirring sign = 1; #define even } #define mouse do #define The { #define stockings rightDigit #define were = number #define hung % #define By 10; #define chimney printf("%d", rightDigit); #define with number /= #define care 10; #define In } #define hopes while #define that (number); #define Saint if (sign) #define Nicholas puts("-"); #define Soon else #define would putchar('\n'); #define be return 0; #define there } Код))) Code: Twas the night before Christmas And all through the house Not a creature was stirring Not even a mouse The stockings were hung By the chimney with care In hopes that Saint Nicholas Soon would be there
лол Code: #define подъёбку setlocale #define чуть_чуть 7 #define так_себе 12 #define пошло_оно_всё 120 #define срака double #define волосатая unsigned long #define фигню фигня #define кидай cin >> #define кончил } #define начал { #define конкретно * #define ну ) #define в_общем ( #define кагбэ [ #define ХУЙ 0 #define да ] #define какая_то int #define какой_то int #define какое_то int #define какие_то int #define давай void #define туды_сюды for #define Слышь_это cout << #define эээ << #define и_ещё_больше ++ #define хуякс / #define Подрыхнуть Sleep #define подвинь_жопу new #define бля endl #define шнягу шняга #define стал = #define стала = #define стало = #define стали = #define взад return #define ну_если_уж if #define убрать_на*** delete #define Закрой_Пасть CloseHandle #define УЁБИЩЕ HANDLE #define стало_похоже_на == #define говно NULL #define присобачить += #define тогда /*WTF*/ #define Жди_Хрен_Дождёшься WaitForSingleObject #define вантуз GetLastError #define ХУИТА main // sic! #define поехали CreateThread #define въёбывай LPTHREAD_START_ROUTINE #define почти < #define норма 1 #define ДОХУЯ INFINITE #include <windows.h> #include <iostream> #pragma warning в_общем disable: 4244 ну using namespace std; какая_то фигня; какие_то маленькое, ОГРОМНОЕ; какие_то Ленин, ЕБАНУТОСТЬ; давай поработай в_общем какая_то конкретно шняга ну начал маленькое стало шняга кагбэ ХУЙ да; какой_то козёл, говнистость; туды_сюды в_общем козёл стал норма; козёл почти фигня; козёл и_ещё_больше ну начал ну_если_уж в_общем шняга кагбэ козёл да почти маленькое ну начал маленькое стало шняга кагбэ козёл да; Ленин стал козёл; Подрыхнуть в_общем так_себе ну; кончил кончил Слышь_это "\n\nМинимальный элемент массива: " эээ маленькое эээ бля; ОГРОМНОЕ стало шняга кагбэ ХУЙ да; туды_сюды в_общем говнистость стало норма; говнистость почти фигня; говнистость и_ещё_больше ну начал ну_если_уж в_общем шняга кагбэ говнистость да > ОГРОМНОЕ ну начал ОГРОМНОЕ стало шняга кагбэ говнистость да; ЕБАНУТОСТЬ стала говнистость; Подрыхнуть в_общем пошло_оно_всё ну; кончил кончил Слышь_это "\n\nМаксимальный элемент массива: " эээ ОГРОМНОЕ эээ бля; кончил какая_то ХУИТА в_общем ну начал подъёбку в_общем ХУЙ, ".1251" ну; Слышь_это "\nВведите размерность массива: \n"; кидай фигню; какая_то конкретно шняга стал подвинь_жопу какая_то кагбэ фигня да; Слышь_это "\nВведите элементы массива: \n"; туды_сюды в_общем какой_то козёл стал говно; козёл почти фигня; козёл и_ещё_больше ну кидай шнягу кагбэ козёл да; волосатая *****; УЁБИЩЕ быдло стало поехали в_общем говно, говно, в_общем въёбывай ну поработай, в_общем давай конкретно ну шняга, ХУЙ, &***** ну; ну_если_уж в_общем быдло стало_похоже_на говно ну тогда взад вантуз в_общем ну; // Если ошибка // Находим среднее арифметическое срака посередине стало шняга кагбэ ХУЙ да; туды_сюды в_общем какая_то козёл стал норма; козёл почти фигня; козёл и_ещё_больше ну начал посередине присобачить шнягу кагбэ козёл да; Подрыхнуть в_общем чуть_чуть ну; кончил посередине стало посередине хуякс фигня; Слышь_это "\n\nСреднее арифметическое элементов массива: " эээ посередине эээ бля; Жди_Хрен_Дождёшься в_общем быдло, ДОХУЯ ну; // Ждём, пока поток поработай закончит работу какая_то писька стала посередине; // Целая часть шняга кагбэ Ленин да стал писька; // Замена минимума шняга кагбэ ЕБАНУТОСТЬ да стала писька; // Замена максимума Слышь_это "\nМассив с заменой минимума и максимума на целую часть среднего арифметического: \n" эээ бля; туды_сюды в_общем какая_то какашка стала говно; какашка почти фигня; какашка и_ещё_больше ну начал Слышь_это шняга кагбэ какашка да эээ " "; кончил Слышь_это бля эээ бля; Закрой_Пасть в_общем быдло ну; // Закрываем дескриптор потока убрать_на*** шнягу; взад ХУЙ; кончил
Трюки от Крыса 20h: юбилейный выпуск Трюки от Крыса 20h: юбилейный выпуск Крис Касперски Наверное, все слышали о языке brainfuck, программирование на котором представляет мазохизм в стиле «хрен напишешь» и «хрен прочтешь». Однако Си с его гибким синтаксисом и мощным препроцессором позволяет трахать мозги не только секса ради, но и для дела. Обфускация исходных текстов – это конкретная тема, которой и посвящен юбилейный выпуск трюков. Это не шутка! Программа для расчета числа «пи». Да-да, именно расчета, а не вывода готовой константы посредством сопроцессора. Чем больше круг — тем выше точность расчетов. Code: /* * Program to compute an approximation of pi * by Brian Westley, 1988 */ #define _ 0xF<00? --F<00||--F-OO--:-F<00||--F-OO--; int F=00,OO=00; main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO() { _-_-_-_ _-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_-_-_-_-_ _-_-_-_-_-_-_-_ _-_-_-_ } Фундамент языка Си трактует перенос строки, как пробел, что открывает практически неограниченный простор для «графитти», даже безо всякого препроцессора. Для достижения поставленной цели хватит и фундаментальных конструкций самого языка. Программа, изображенная ниже, похожа на первоапрельскую шутку, которой она по сути дела и является. Код исправно компилируется и работает, выводя на экран число «минус два», а если последнюю строку заменить на «1 -+- + -+- 1», мы получим… ноль. Графитти на чистом Си Code: foo(int a){printf("%d\n",a);};main(){foo( +- -+- -+- -+ ////////////.! ////////////./! ////////////.//! ////////////.///! + - -+- -+- +////! !!!!!~~!!!!!!////! !!!!~~!~!!!!!////. !!!~~~~~~!!!!///// !!~~!~~!~~!!!//// !~~!!~~!!~~!!/// ~~!!!~~!!!~~!// 1 -+- - -+- 1 ); } И в чем прикол? А в том, что никакой программист не сможет быстро сказать, какой результат мы получим на выходе. К тому же, подобные «художества» автоматически трактуются человеком как комментарии, что позволяет скрыть часть функционала. Не говоря уже о том, что после наведения «порядка» и удаления всех «ненужных» комментариев программа просто развалится со всеми вытекающими отсюда… Реальный хардкор Строго говоря, приведенные выше примеры обфускацией не являются и при наличии мозгов снимаются в два счета. Хорошо, держите программу из трех строк, над анализом алгоритма работы которой можно трахаться часами, причем математика здесь и не ночевала. Всего лишь тривиальные операции с указателями: Программа-головоломка (это только с виду она простая…) Code: /* * HELLO WORLD program * by Jack Applin and Robert Heckendorn, 1985 */ main(v,c)char**c;{for(v[c++]="Hello, world!\n)"; (!!c)[*c]&&(v--||--c&&execlp(*c,*c,c[!!c]+!!c,!c)); **c=!c)write(!!*c,*c,!!**c);} Вопрос на засыпку — что это такое, как оно работает и что делает? Если кто думает, что программа выводит строку «Hello, world», он глубоко ошибается. Благодаря коварному багу, на экране отображается только «Hello», а «world!» появляется только после удаления пробела между запятой и миром. Кстати говоря, 128 байт исходного текста транслируются оптимизирующим компилятором Microsoft Visual C++ в 128 байт машинного кода. Мистика! Совпадение размеров, разумеется, чистая случайность (в 1985 году, где создавалась эта программа, никаких «визуалов» вообще не существовало), но черной магии здесь и без того хватает. Концентрация языковых «извращений» просто поражает! Начнем с того, что программа рекурсивно вызывает сама себя посредством документированной, но малораспространенной функции execlp(). Это серьезно затрудняет отладку, поскольку отладчики на работу с дочерними процессами как-то не рассчитаны, и пошаговая остановка тихо кончается, не достигая цели. Точки останова, установленные в материнском процессе, на дочернее потомство, естественно, не распространяются. Заниматься оплодотворением приходится самостоятельно, прибегая к рукоблудному методу инъекции бряков непосредственно в тело исполняемого файла, что съедает все свободное и несвободное время, без всякой надежды на скорый успех. Во многих случаях обфускация снимается декомпиляцией двоичного файла в Си-код, повторной компиляцией декомпилированного файла с максимальным уровнем оптимизации и последующей финальной декомпиляцией. В качестве декомпилятора обычно используется Hex-Rays, который люди берут так же, где и саму ИДУ – не потому, что Hex-Rays хорош, а потому, что другие декомпиляторы еще хуже. Однако, в нашем случае Hex-Rays генерирует кривой код, повторная компиляция которого разваливает программу. Причем, понять, где она валится, практически невозможно – алгоритм в целом тривиален, но при переходе на частности выясняется, что любая конкретно взятая операция (или даже группа операций) в изоляции от остальных лишена смысла, и декомпозиция идет лесом. Программа представляет собой монолитное сооружение, своеобразный магический кристалл с множеством граней, многократно отражающихся друг в друге, и совокупность этих отражений реализует алгоритм, расшифровав который мы узнаем, что программа считает первый аргумент командой строки, укорачивает его на один символ и выводит результат на экран. Поскольку пробел трактуется как разделитель аргументов, то оригинальный вариант программы оказывается неработоспособен, и нам необходимо либо удалить пробел после запятой, либо заключить строку в кавычки, либо сделать и то, и другое сразу: Результат работы оптимизирующего компилятора с последующей декомпиляцией Hex-Rays – те же яйца, только в профиль Code: int __cdecl main(int argc, const char **argv, const char *envp) { int result; int v4; bool v5; char **v6; int v7; v4 = argc; argv[argc] = "Hello, world!\n)"; v6 = (char **)(argv + 1); result = argv + 1 != 0; v5 = argv + 1 != 0; if ( (*(argv + 1))[result] ) { do { v7 = v4--; if ( !v7 ) { --v6; if ( !v6 ) break; v5 = 1; result = _execlp(*v6, (char)*v6); if ( !result ) break; } _write(*v6 != 0, *v6, **v6 != 0); result = (int)*v6; **v6 = v6 == 0; } while ( (*v6)[v5] ); } return result; } http://www.xakep.ru/magazine/xa/121/102/1.asp
мож не в тему, а может и да ) лексический анализатор на php PHP: <?php $text="int a=1; int b=22; int c=6; str hi=\"hello %d\"; c=a+b*c; print(hi,c);"; $types=array( 'ttResWord', //ключевое (зарезервированное) слово 'ttOperator', //оператор 'ttStrConstant', //строковая константа 'ttIntConstant', //числовая константа 'ttVariable', //переменная 'ttFunction', //функция 'ttDevider', //разделитель 'ttUnknown' //тип не определен, либо не известен ); $lexem=array( 0=>"/(int|str)\s/", 1=>"/(\s*[=|+|-|\*|\/]\s*)/", 2=>"/(\".*\")/", 3=>"/=(\s*[0-9]*)[=|+|-|\*|\/|;|,|\)]?/", 4=>"/([a-z|A-Z]*?)\s*[=|+|-|\*|\/|;|,|\)]/", // пэпэц 5=>"/(print)/", 6=>"/(;|,|\(|\))/" ); echo "<h3>Исходный код</h3><pre><code>$text</code></pre>\n<h3>Лексический анализатор</h3>"; $offset=0; $token=array(); $arr=explode("\n",$text); for($i=0;$i<count($arr);$i++){ foreach($lexem as $type=>$pattern){ if(preg_match_all($pattern,$arr[$i],$f,PREG_OFFSET_CAPTURE)) { $l=0; foreach($f[1] as $k=>$v){ //echo $v[0] . $v[1]."<br>"; $name=$f[1][$l++][0]; if($type!=2) $name=str_replace(' ','',$name); if($name) { if($type==4 && preg_match($lexem[0],$name." ")) { continue; } echo "[".$i." : ".$v[1]."] '<b>".$name."</b>' : ".$types[$type]."<br />"; $token[$offset+$v[1]]=array($type,$i,$name); } //echo "<tr><td>".$i."</td><td>".$v[1]."</td><td>".$f[1][$l++][0]."</td><td>".$types[$type]."</td></tr>"; } } } $offset+=strlen($arr[$i]); } echo "<h3>Синтаксический анализатор</h3>"; ksort($token); $tt=array(); $c=0; foreach($token as $k=>$v){$tt[]=array($v[0],$v[1],$v[2]);$c++;} //unset($token); $err=''; for($i=0;$i<$c;$i++) { if(!empty($err)) { exit("<b>Ошибка</b>: ".$err." // на линии <b>".($tt[$i-1][1]+1)."</b>");break;} switch($tt[$i][0]) { case 0: // если это обьявление типа //echo $tt[$i+1][0]."<br>"; if($tt[$i+1][0]!=4) { // если после него не идет обьявление переменной то это ошибка $err="не объявлена переменная"; break; } // если после обявления переменной нихрена не оператор присваивания... if($tt[$i+2][2]!="=") { $err="переменную сначала необохдимо присвоить, например a=5"; break; } // а где переменная? if($tt[$i][2]=='str' && $tt[$i+3][0]!=2) { $err="в объявлении строковой переменной"; break; } if($tt[$i][2]=='int' && $tt[$i+3][0]!=3) { $err="в объявлении целой переменной"; break; } if($tt[$i+4][0]!=6){ $err="необходим разделитель";break;} break; } } echo "Ошибок нет<br /><h3>Интерпритатор</h3>"; $code=''; foreach($token as $k=>$v) { if($v[0]==0){$v[2]='';} if($v[0]==4){$v[2]="$".$v[2];} if($v[2]==';'){$v[2].="\n";} if($v[0]==5){$v[2]=str_replace("print","printf",$v[2]);} $code.=$v[2]; } $code.=''; eval($code); ?>