Rich сигнатура, или что скрывает MS компилятор Начало: Наверное многие кодеры, да и просто любопытные люди сталкивались с тем что в некоторых exe/dll/sys и тому подобных файлах присутствуют непонятные данные между MZ и PE заголовком, которые заканчивались словом Rich. Многие не обращали на них внимания, некоторые же по ним могли сказать что данный файл был создан с использованием компилятора от Microsoft. Также были люди кто считали что там спрятаны какие-то данные нужные для работы программы. И это лишь только одна сторона данного факта. Другая сторона - эта то, что многие знают о том, что Microsoft специальным образом помечает исполняемые файлы, созданные с помошью их компиляторов (С\С++\MASM) и что якобы по это сделано для того, чтобы вычислить создателей вредоносных программ. Многие полагают что при линковке вписывается информация о компьютере или пользователе. Объединяя оба этих факта можно с уверенностью сказать что Rich данные - это как раз и есть тот идентификатор по которому можно определить человека / компьютер где была создана вредоносная программа. Так что наша цель - проверить, так ли это на самом деле или же многие ошибаются. Rich в подробностях: Из основных особенностей этих данных которые могли бы быть найдены при визуальном осмотре можно выделить следующие: 1) Они всегда идут после MZ заголовка, но перед PE заголовком 2) Чаще всего их положение относительно начала файла = 80h = 128 3) Структура Rich данных примерно такова: XXXXXXXX - YYYYYYYY - YYYYYYYY - YYYYYYYY XXXXXXXX - XXXXXXXX - XXXXXXXX - XXXXXXXX XXXXXXXX - XXXXXXXX - XXXXXXXX - XXXXXXXX [Rich сигнатура] - YYYYYYYY где YYYYYYYY - Повторяющиеся 32-х битные значения XXXXXXXX - постоянно разные 32-х битные значения [Rich сигнатура] - это 4 байта образующие слово Rich т.е. примерно такие: 4) также можно заметить что программа скомпиленные на одном и томже компе имеет одинаковые Rich данные Что скрывает Rich: Было потрачено много времени на то, чтобы узнать что хотябы какую нибудь информацию и том, что за данные там хранятся и в итоге был найден забугорный сайт, где была расположена статья (http://ntcore.com/files/richsign.htm) человека под именем Daniel Pistelli в которой он исследовал данную сигнатуру, а также то, как она формируется и еще очень много полезных данных. (P.S. Если знаете забугорный язык, то советую почитать эту статейку, много интересного можно прочесть по поводу Rich данных). Но в принципе нам не важно пока что как были получены эти данные, нас интересует то, что там скрыто. Как было выяснено в вышеуказанной статье число, помеченное нами как YYYYYYYY - это есть ключ шифрования/дешифрования. Если говорить более подробно об этом, то это всеголишь маска для XOR операции(которая как раз и представляет собой шифрование) И так перейдем к расшифровке: 1) берем двойное слов после сигнатуры Rich - это наш ключ. 2) и теперь каждое двойное слово Rich данных XOR'ом на это число и так до тех пор пока не дойдем до сигнатуры Rich (её XOR'ить не надо). В результате получаем чтото типа такого DanS-00000000 - 00000000 - 00000000 XXXXXXXX - XXXXXXXX - XXXXXXXX - XXXXXXXX XXXXXXXX - XXXXXXXX - XXXXXXXX - XXXXXXXX Rich-YYYYYYYY где, XXXXXXXX - расшифрованные данные 00000000 - число XOR на само себя = 0. по этому мы и получаем его. Сигнатура DanS скорее всего была вписана для того, чтобы проверить что то точно Rich данные и что они не подделаны простым рандомом. Далее автор той статьи прогнал компилятор через IDA чтобы более детально изучить то, что записано в Rich данных. Как оказалось их формат такой: XXXX00YY ZZZZZZZZ XXXX00YY ZZZZZZZZ XXXX00YY ZZZZZZZZ XXXX00YY ZZZZZZZZ где, XXXX - старшая версия YYYY - младшая версия ZZZZZZZZ - чтото типа версия билда. И так, для тех кто еще не понял, то Rich данные - это всеголишь версии библиотек и компилятора, которые были использованы при создании программы. Практическая реализация: Всё бы то хорошо, но всегда лучше увидеть своими глазами что за версии там находятся. да и хотелось бы автоматизировать данный процесс. По этому напишем на Delphi небольшую функцию которая будут выводить версии либ зашитые в Rich данных. Code: // на входе имя и путь до файла procedure PrintRichData(FileName:string); var Lib : DWORD; // адрес файла в памяти Data : DWORD; // данные Key : DWORD; // ключ шифрования x : integer; cnt : integer; // кол-во элементов в данных MinVer, MajVer : WORD; // версия Times : DWORD;// доп инфо. Msg : string; begin Msg := ''; // загрузим в память подопытную прогу Lib := LoadLibrary(PAnsiChar(FileName)); if Lib <> 0 then // если загрузили begin cnt := 0; while true do // перебираем Rich данные чтобы найти конец и кол-во их begin // получаем текущую запись Data := DWORD(pointer(Lib + $80 + (cnt shl 2))^); if Data = 0 then break; // если пустое значение значит нету данных if Data = $68636952 then break; // проверим на конец данных - сигнатура Rich inc(cnt); // переходим на следующую запись end; if cnt <> 0 then // если есть Rich данные begin // считаем маску шифрования Key := DWORD(pointer(Lib + $80 + ((cnt+1) shl 2))^); x := 4; // Так как первый элемент это DanS а потом 3 повтора ключа, то начинаем сразу с 4-го элемента while x < cnt-1 do // перебираем все элементы begin Data := DWORD(pointer(Lib + $80 + (x shl 2))^) xor Key; // расшифровываем MinVer := Data and $FFFF; // младшая версия MajVer := (Data shr 16) and $0F; // старшая inc(x); Times := DWORD(pointer(Lib + $80 + (x shl 2))^) xor Key; // доп. инфа. Msg := Msg + 'Ver: ' + inttostr(MajVer) + '.0.' + inttostr(MinVer) + ' Times:' + inttostr(Times) + #13#10; inc(x); // следующий элемент end; MessageBox(0, PAnsiChar(Msg), 'INFO', 0); end; FreeLibrary(Lib); end; end; В результате выполнения данной функции можно получить данные о версиях. В моём случае подопытный файлы имел следующие версии либ Чем это нам грозит: И выше описанных данных можно сказать, что никакой уж важной и темболее конфеденциальной информации Rich данные не хранят, по этому не стоит волноваться по поводу того что они будут присутствовать в ваших программах. Хотя на деле можно выявить следующие ключевые моменты: 1) Можно узнать какая версия компилятора использовалась 2) Одна и таже версия компилятора на разных компах. для одно и той же проги будет давать одинаковые Rich данные. Так что по этим данным нельзя на прямую доказать что данная программа создана на этом компьютере и тем более вами. 3) С другой стороны т.к. Маска шифрования вычисляется исходя из данных PE заголовка, то антивирусы могут использовать её как сигнатуру 4) Удаление этой сигнатуры - может вызвать больший интерес антивируса к вашей программе. 5) Рандомные значения в данных о версиях тоже могут привлечь внимания антивирусов. 6) При написании крипторов, если используются вообще полностью рандомные Rich данные, то антивири сразу могут понять что файл был изменен т.е. провалится проверка по второй сигнатуре (DanS) Так что лучше если использовать эти данные, то желательно иметь набор самый распространенных версий либ. Советую брать их из исходников программы PE file "Rich" fucke написанной Vovane (http://www.wasm.ru/forum/viewtopic.php?id=8572) 7) Если пишите криптер и косити под MS компилятор то не забывайте про Rich данные и их корректность. Заключение: Теперь выяснив всё, можно с уверенность сказать что Rich ничего про нас не расскажет и можно спать спокойно ) (С) Копирайты всего этого: 1) SLESH (Antichat.ru) 2) Daniel Pistelli (http://ntcore.com/files/richsign.htm) - Подробное описание Rich данных. 3) Vovane (http://www.wasm.ru/forum/viewtopic.php?id=8572) - Программка подделки и проверки Rich данных.
Тема, как отучить линкер внедрять эту подпись http://wasm.ru/forum/viewtopic.php?pid=306196 Патчи для MSVS2008 & MSVS2008 SP1 vs2008.x86.linker.patch.zip - http://wasm.ru/forum/attachment.php?item=1816 vs2008sp1.x86.linker.patch.zip - http://wasm.ru/forum/attachment.php?item=2674
Если ты убираешь у своей проги Rich сигнатуру, но при этом остаётся стандартная сишная структура проги, то это вызывает подозрение у некоторых антивирей и они более тщательно начинают проверять файл
Переложите плиз файл, если остался у кого (1577210318__RichFuke.rar). Vovane (http://www.wasm.ru/forum/viewtopic.php?id=8572) - Программка подделки и проверки Rich данных.
Мой вариант на Си Code: int _tmain(int argc, _TCHAR* argv[]) { if (argc <= 1) { printf( "Error: No arguments\n"); return 0; } FILE * stream; if( (stream = _wfopen( argv[1], L"r+b" )) == NULL ) { printf( "Error: The file %s was not opened\n", argv[1]); return 0; } fseek(stream, 0, SEEK_END); DWORD sizefile = ftell(stream); BYTE * pFile = (BYTE *)malloc(sizefile); fseek(stream, 0, SEEK_SET); fread(pFile, sizefile, 1, stream); fclose(stream); DWORD data; DWORD i; for (i=0; i<1000; i+=4) { data = *(DWORD *)(pFile + i + 0x80); if (data == 0) { printf( "Error: Rich-data not found\n"); return 0; } if (data == 0x68636952) break; } DWORD key = *(DWORD *)(pFile + i + 0x80 + 4); DWORD x = 4*4; WORD MinVer; WORD MajVer; DWORD times; while(true) { if (*(DWORD *)(pFile + x + 0x80) == 0x68636952) break; data = *(DWORD *)(pFile + x + 0x80) ^ key; MinVer = data & 0xFFFF; MajVer = (data >> 16) & 0x0F; x += 4; times = *(DWORD *)(pFile + x + 0x80) ^ key; printf("Ver: %d.0.%d Times: %d\n", MajVer, MinVer, times); x += 4; } return 0; } Запускать с параметром filename.