Задумался я тут над смыслом подписания компоновочных блоков .Net. Наверняка,
ты тоже подписывал свои библиотеки, чтобы установить их в GAC. В ходе
сегодняшнего расследования мы научимся изменять подписанные сборки, не обладая
исходниками и секретными ключами.
Приватные сборки
При подписании библиотеки (назначении строгого имени) открытый ключ
записывается в манифест. Таким образом, чтобы внести изменения в чужую
подписанную библиотеку, нужно просто заменить публичный ключ на свой. Или — еще
проще — сделать новую сборку с таким же именем и подписать на своем ключе.
При использовании библиотеки любой публичный ключ будет принят как
доверенный.
Проведем эксперимент, создадим небольшую библиотечку:
Библиотека signedLib.dll
namespace signedLib
{
public class sLib
{
public static int GetNumber() { return 1; }
}
}
Подпишем ее и добавим к проекту консольного приложения:
Консольное приложение changeKey.exe
namespace changeKey
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(signedLib.sLib.GetNumber());
Console.ReadLine();
}
}
}
Затем скомпилируем релиз проекта. С помощью .NET Reflector [1] и плагина
Reflexil [2] отредактируем IL-код подписанной библиотеки (signedLib.dll), так
что GetNumber() будет возвращать не "1", а "2". Консольное приложение не
заметило подмены и вывело "2". Вывод: подменить/изменить приватную сборку со
строгим именем очень просто. Другие сборки, ссылающиеся на измененную, никак на
это не реагируют, несмотря на то, что они были скомпилированы с оригинальной.
Обращаю внимание, что речь идет именно о приватных сборках, со сборками в GAC
дело обстоит иначе.
Сборки в GAC
Как мы только что убедились, подписанные приватные компоновочные блоки можно
легко модифицировать. При этом необязательно обладать ни исходниками, ни
секретным ключом, ни правами администратора.
Вносить изменения в сборки, установленные в GAC, не многим сложнее. В случае
с приватными сборками их "подписанность" никакой роли не играет. Подпись не
проверяется, а "полный идентификатор приватного компоновочного блока состоит из
имени компоновочного блока и числового номера его версии" (из книги Э.
Троелсена).
Сборки, устанавливаемые в GAC, должны иметь так называемое строгое имя.
Сборка получает строгое имя в момент ее подписания. Идентификаторы сборок в GAC
дополняются параметрами публичного ключа, подписи проверяются. Замуровали,
демоны! Что же это получается? Окружили со всех сторон:
- незаметно внести изменения не получится - подпись проверку не пройдет;
- свой публичный ключ не подсунешь - идентификатор сборки изменится.
Тем не менее, не нужно быть криптографом, чтобы все же изменить библиотеку в
GAC. Требуется всего лишь обладать правами администратора и знать параметры
утилиты sn.exe (страдальцы, не имеющие Студии, вручную используют стандартную
утилиту sn.exe для подписания компоновочных блоков).
Итак, возьмем проект уже знакомой библиотеки signedLib.dll. Подпишем ее и
установим в GAC.
gacutil.exe /i
D:\projects\changeKey\signedLib\bin\Release\signedLib.dll
Добавим референс к консольному приложению changeKey.exe. Компилируем релиз,
убеждаемся, что в папке с программой нет файла signedLib.dll (это значит, что
сборка будет загружена из GAC). Запускаем changeKey.exe - приложение показывает
"1".
С этого момента воображаем себя атакующими - у нас нет исходников, нет
секретного ключа. Но надо, чтобы метод GetNumber() возвращал не 1, а 2.
Структуру файлов ниже C:\Windows\assembly проводник Windows не показывает.
Создадим псевдодиск, на который будет проецироваться нужный каталог:
subst b: C:\Windows\assembly
В проводнике появился диск B.
.Net сборки попадают в папку GAC_MSIL; находим нужную папку (ее название
совпадает с названием .dll файла). Внутри будет еще одна папка, а в ней,
наконец, signedLib.dll. Копируем signedLib.dll на рабочий стол.
С помощью замечательной программы .NET Reflector и не менее замечательного
плагина Reflexil (на нашем диске все это
хозяйство тебя уже заждалось) мы будем редактировать библиотеку. Предварительно
перепишем токен публичного ключа и его значение в блокнот (пригодятся позже).
Как мы уже знаем, публичный ключ записан в самой сборке, и теперь в этом можно
окончательно убедиться.
После правки IL-кода и сохранения изменений программа сообщит о том, что
цифровая подпись нарушена и предложит варианты дальнейших действий.
Нажимаем "Remove Strong Name" - удалить цифровую подпись. Закрываем сборку
(теоретически закрывать сборку нет необходимости и нам должен подойти вариант
"Register it for verification skipping", однако у меня эта операция
заканчивается ошибкой; к тому же, в обучающих целях лучше проделать все операции
вручную).
Теперь у нас есть:
- Измененная, не подписанная dll;
- публичный ключ оригинальной библиотеки.
Осталось установить ее в GAC. Для этого воспользуемся механизмом отложенной
подписи. Если сборка содержит информацию о публичном ключе, но не имеет цифровой
подписи – говорят, что она имеет отложенную подпись (придумал это какой-то
надмозг из Майкрософт "с целью тестирования").
Сделать такую сборку с помощью .NET Reflector не составляет никакой сложности
- нужно заполнить соответствующие поля, они выделены желтым на рисунке
"Параметры публичного ключа" (помнишь, мы копировали их значения в блокнот?). И
не забудь поставить галочку "HasPublicKey" (в теории публичный ключ нужно
извлекать из секретного с помощью утилиты sn.exe и потом с помощью нее же
создавать отложенную подпись).
Итак, мы получили сборку, которая называется так же, как оригинальная, имеет
такую же версию и такой же публичный ключ. Получается, если ее установить в GAC,
она получит точно такой же идентификатор, что и оригинальная (смотри начало
статьи). Как я писал выше, по умолчанию у сборок в GAC проверяется подпись,
однако проверку подписи можно отключить - опять же "для тестирования".
Чтобы отключить проверку подписи dll на данном компьютере, нужно
воспользоваться sn.exe:
sn -Vr C:\Users\Alex\Desktop\signedLib.dll
Удаляем оригинальную сборку из GAC:
gacutil /u
signedLib,Version=1.0.0.0,Culture=neutral,PublicKeyToken=2b1b71846e76146e
И устанавливаем измененную:
gacutil /i C:\Users\Alex\Desktop\signedLib.dll
Радуемся, глядя на выведенную gacutil.exe надпись:
Assembly successfully added to the cache
Вот мы и добились желаемого - изменили библиотеку, установленную в GAC. Чтобы
еще раз порадоваться (и проверить результат), запускаем наше приложение
changeKey.exe, которое в начале статьи выводило 1. Ура, теперь он покажет 2!
Подведем итог
Самое время подвести итог нашим сегодняшним свершениям. Сделаем это по
пунктам:
- Публичный ключ записан в самой сборке (точнее, в манифесте);
- в случае с приватными сборками подписи не проверяются.
Чтобы изменить сборку в CAG, нужно:
- Сделать копию нужного dll-файла из C:\Windows\assembly (воспользовавшись
командой subst).
- Извлечь из сборки публичный ключ.
- Модифицировать IL-код сборки и удалить цифровую подпись.
- Добавить к измененному файлу публичный ключ, полученный на шаге 2
(создадим отложенную подпись).
- Отменить проверку цифровой подписи для модифицированной сборки на данном
компьютере.
- Удалить оригинальную сборку из GAC.
- Установить модифицированную сборку.
Для реализации шагов 5-7 нужно обладать правами администратора. Вот и все! На
этом позволь откланяться и пожелать тебе огромных творческих узбеков на ниве
исследований программного обеспечения.
INFO
Для комфортного чтения статьи нужно обладать базовыми знаниями в области
криптографии с открытым ключом.
|