#18 Gray Hat C#. Руководство для хакера по созданию и автоматизации инструментов безопасности. Создание класса для документа WSDL.
Здравствуйте, дорогие друзья.
При программном анализе WSDL, проще всего начать с верхней части документа, с типов SOAP и двигаться вниз по документу. Давайте создадим класс под названием WSDL, который будет включать в себя документ WSDL. Конструктор относительно прост, как показано в ниже.
Конструктор нашего WSDL-класса вызывает всего несколько методов (которые мы напишем далее) и ожидает в качестве параметра полученный XML-документ, содержащий все определения веб-сервиса. Первое, что нам нужно сделать, — это определить пространства имен XML, на которые мы будем ссылаться, при использовании запросов XPath (которые описаны в листинге 3-3 и последующих листингах), когда мы реализуем методы синтаксического анализа. Для этого мы создаем новый XmlNamespaceManager [1], и используем метод AddNamespace() [2], чтобы добавить два пространства имен: wsdl и xs. Затем мы вызываем методы, которые будут анализировать элементы документа WSDL, начиная с типов и заканчивая сервисами. Каждый метод принимает два аргумента: документ WSDL и менеджер пространства имен.
Нам также необходим доступ к некоторым свойствам класса WSDL, которые соответствуют методам, вызываемым в конструкторе. Добавьте свойства, показанные в листинге ниже, в класс WSDL.
1 2 3 4 5 |
public List Types { get; set; } public List Messages { get; set; } public List PortTypes { get; set; } public List Bindings { get; set; } public List Services { get; set; } |
Эти свойства класса WSDL используются фаззером (поэтому они общедоступны), и методами, вызываемыми в конструкторе. Свойства представляют собой списки классов SOAP, которые мы реализуем в этом разделе.
Написание начальных методов синтаксического анализа
Сначала мы напишем методы, которые вызываются в листинге 3-1. Как только мы реализуем эти методы, мы перейдем к созданию классов, от которых зависит каждый метод. Это будет непростая работа, но мы справимся вместе!
Мы начнем с реализации первого метода, вызванного в листинге 3-1, ParseTypes(). Все методы, вызываемые из конструктора, относительно просты и выглядят аналогично листингу ниже.
Поскольку эти методы вызываются только внутри конструктора WSDL, мы используем ключевое слово Private, чтобы доступ к ним мог получить только класс WSDL. Метод ParseTypes() принимает в качестве аргументов документ WSDL и менеджер пространства имен (используемый для разрешения пространств имен в документе WSDL). Затем мы создаем экземпляр нового объекта List и присваиваем его свойству Types. Затем мы перебираем элементы XML в WSDL, используя возможности XPath, доступные для документов XML в C#. XPath позволяет программисту просматривать и использовать XML-документ на основе путей к узлам внутри документа.
В этом примере мы используем запрос XPath для перечисления всех узлов типа SOAP из документа с помощью метода SelectNodes(). Затем мы перебираем эти типы SOAP и передаем каждый узел конструктору класса SoapType, который является одним из классов, которые мы реализуем после ввода начальных методов синтаксического анализа. Наконец, мы добавляем вновь созданные объекты SoapType в свойство списка SoapType класса WSDL.
Достаточно легко, правда? Мы будем использовать этот шаблон использования запроса XPath для повторения определенных узлов еще несколько раз, чтобы использовать несколько других типов узлов, которые нам нужны из документа WSDL. XPath довольно мощный инструмент и отлично подходит для работы с языком C# в целом. Теперь мы реализуем следующий метод, вызываемый в конструкторе WSDL для анализа документа WSDL, ParseMessages(), как подробно описано в листинге ниже.
Во-первых, нам нужно создать экземпляр и назначить новый список для хранения объектов SoapMessage. (SoapMessage — это класс, который мы реализуем в разделе «Создание класса SoapMessage для определения отправленных данных» на странице 60.) Используя запрос Xpath [1] для выбора узлов сообщения из документа WSDL, мы перебираем узлы, возвращаемые методом SelectNodes(). и передайте их конструктору SoapMessage. Эти вновь созданные объекты добавляются в свойство Messages класса WSDL для последующего использования.
Следующие несколько методов, вызываемых из класса WSDL, аналогичны двум предыдущим. К этому моменту они должны показаться вам относительно простыми, учитывая, как работали два предыдущих метода. Все эти методы подробно описаны в листинге ниже.
Чтобы заполнить свойства PortTypes, Bindings и Services, мы используем запросы XPath для поиска и перебора соответствующих узлов; затем мы создаем экземпляры определенных классов SOAP, которые мы реализуем дальше, и добавляем их в списки, чтобы мы могли получить к ним доступ позже, когда нам понадобится построить логику фаззера WSDL.
Вот и все, что касается класса WSDL. Конструктор, несколько свойств для хранения данных, относящихся к классу WSDL, и некоторые методы для анализа документа WSDL — это все, что Вам нужно для начала работы. Теперь нам нужно реализовать вспомогательные классы. В методах синтаксического анализа мы использовали некоторые еще не реализованные классы (SoapType, SoapMessage, SoapPortType, SoapBinding и SoapService). Начнем с класса SoapType.
Написание класса для типа и параметров SOAP
Чтобы завершить метод ParseTypes(), нам нужно реализовать класс SoapType. Класс SoapType является относительно простым. Все, что ему нужно, — это конструктор и пара свойств, как показано в листинге ниже.
Логика в конструкторе SoapType аналогична логике предыдущих методов анализа (в листингах 3-4 и 3-5), за исключением того, что мы не используем XPath для перечисления узлов, которые мы обходим. Могли бы, но я хотел показать Вам другой способ перебора узлов XML. Обычно при анализе XML лучше всего использовать XPath, но XPath может быть дорогостоящим в вычислительном отношении. В этом случае мы напишем оператор if, чтобы проверить, нужно ли нам перебирать дочерние узлы. Перебор дочерних узлов с использованием цикла foreach для поиска соответствующего элемента XML требует немного меньше кода, чем использование XPath в этом конкретном случае.
Класс SoapType имеет два свойства: свойство Name, которое представляет собой строку, и список параметров (класс SoapTypeParameter, который мы вскоре реализуем). Оба этих свойства используются в конструкторе SoapType и являются общедоступными, поэтому их можно использовать вне класса позже.
Мы используем свойство Attributes [1] узла, передаваемого в аргументы конструктора, чтобы получить атрибут имени узла. Значение атрибута name присваивается свойству Name класса SoapType. Мы также создадим экземпляр списка SoapTypeParameter, и назначим новый объект свойству «Параметры». Как только это будет сделано, мы используем оператор if, чтобы определить, нужно ли нам в первую очередь перебирать дочерние узлы, поскольку мы не используем XPath для перебора каких-либо дочерних узлов. Используя свойство HasChildNodes [2], которое возвращает логическое значение, мы можем определить, нужно ли нам перебирать дочерние узлы. Если у узла есть дочерние узлы и если у первого дочернего узла этого узла также есть дочерние узлы, мы будем перебирать их.
Каждый класс XmlNode имеет свойство FirstChild и свойство ChildNodes [4], которое возвращает перечисляемый список доступных дочерних узлов. В цикле foreach мы используем цепочку свойств FirstChild [3], для перебора дочерних узлов первого дочернего элемента переданного узла.
Пример узла XML, который будет передан в конструктор SoapType, показан в листинге ниже. После перебора соответствующих дочерних узлов в переданном узле SoapType, мы создаем экземпляр нового класса SoapTypeParameter, передавая текущий дочерний узел в конструктор SoapTypeParameter. Новый объект сохраняется в списке параметров для доступа в дальнейшем.
Теперь давайте создадим класс SoapTypeParameter. Класс SoapTypeParameter также относительно прост. Фактически, никаких переборов дочерних узлов не требуется, и нужен только базовый сбор информации, как показано в листинге ниже.
Пример узла XML, передаваемого конструктору SoapTypeParameter, показан в листинге ниже.
1 |
<xs:element minOccurs="0" maxOccurs="1" name="username" type="xs:string"/> |
Учитывая такой XML-узел, мы можем ожидать, что в нашем методе произойдет несколько вещей. Во-первых, это очень простой параметр WSDL, который определяет параметр с именем username, имеющий строковый тип. Это может произойти минимум ноль раз и максимум один раз. Посмотрите внимательно на код в листинге 3-8, и Вы заметите, что здесь есть оператор if [1], который проверяет значение maxOccurs. В отличие от minOccurs, maxOccurs может быть либо целым числом, либо неограниченным строковым значением, поэтому нам нужно проверить значение maxOccurs перед передачей его в метод int.Parse(), чтобы узнать, какое это значение.
В нашем конструкторе SoapTypeParameter мы сначала присваиваем свойство MaximumOccurrence на основе атрибута maxOccurs узла. Затем мы назначаем свойства минимума, имени и типа на основе соответствующих атрибутов узла.
На этом все. Всем хорошего дня!