-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Перевод] Стек, который позволил Medium обеспечить чтение на 2.6 тысячелетия

Среда, 12 Июля 2017 г. 16:01 + в цитатник

Предлагаю общественности мой перевод статьи Dan Pupius'а об архитектура сервиса Medium и используемых технологиях. Хочу особо отметить, что статья является переводом, поэтому местоимение "я", используемое в тексте далее относится к автору оригинального текста, а не к переводчику.


Фон


Medium это сеть. Это место, где обмениваются историями и идеями, которые важны — место, где вы развиваетесь, и где люди провели 1.4 миллиарда минут — 2.6 тысячелетия.
У нас более 25 миллионов уникальных читателей в месяц, и каждую неделю публикуются десятки тысяч постов. Но мы хотим, чтобы на Medium мерилом успеха было не количество просмотров, а точки зрения. Чтобы значение имело качество идеи, а не квалификация автора. Чтобы Medium был местом, где обсуждения развивают идеи, а слова по-прежнему важны.
Я руковожу инженерной командой. Раньше я работал в качестве инженером в Google, где я работал над Google+ и Gmail, а также был одним из со-основателей проекта Closure. В прошлой жизни я гонял на сноуборде, прыгал из самолёта и жил в джунглях.


image


Команда


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


Команды имеют много свободы в том, как они организуют работу, однако как компания в целом, мы ставим квартальные цели и поощряем итеративный подход. Мы используем GitHub для код-ревью и отслеживания багов, а также Google Apps для электронной почты, документов и электронных таблиц. Мы интенсивно используем Slack — и его ботов — а многие команды используют Trello.


Первоначальный стек


С самого начала мы использовали EC2. Основные приложения были написаны на Node.js, и мы мигрировали на DynamoDB перед публичным запуском.


У нас был node-сервер для обработки изображений, который делегировал GraphicsMagick фактические задачи по обработке. Другой сервер выступал в роли обработчика очереди SQS, и выполнял фоновые задачи. Мы использовали SES для электронной почты, S3 для статичных ресурсов, CloudFront в качестве CDN и nginx как обратный прокси. Для мониторинга мы использовали DataDog и PagerDuty для оповещений.


В качестве основы редактора, используемого на сайта был TinyMCE. К моменту запуска мы уже использовали Closure Compiler и некоторые компоненты Closure Library, но для шаблонизации использовали Handlebars.


Текущий стек


Для такого, на первый взгляд, простого сайта как Medium, может быть удивительно, как много сложного находится за сценой. Это же просто блог, так? Вы, вероятно, можете просто выкатить что-то на Rails за пару дней. :)


В любом случае, довольно рассуждений. Начнём с самого низа.


Производственное окружение


Мы используем Amazon Virtual Private Cloud. Для управление конфигурацией мы используем Ansible, что позволяет хранить конфигурации в системе управления версиями и легко и контролируемо выкатывать изменения.


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


Наше основное приложение по-прежнему написано на Node, что позволяет нам использовать один код как на сервере, так и на клиенте, так, кое-что мы активно используем как в редакторе, так и в обработке поста. С нашей точки зрения Node не плох, но проблемы возникают если мы блокируем цикл обработки событий (event loop). Чтобы сгладить эту проблему, мы запускаем несколько экземпляров на одной машине и распределяем "дорогие" подключения по разным экземплярам, изолируя их таким образом. Мы также обращаемся к среде исполнения V8, чтобы понять, какие задачи занимают много времени; в целом, задержки связаны с восстановлением объектов при десериализации JSON.


У нас есть несколько вспомогательных сервисов, написанных на Go. Мы обнаружили, что приложения на Go очень легко собирать и развёртывать. Нам нравится его типобезопасность без избыточности JVM и необходимости тонкой настройки, как в случае с Java. Лично я — фанат использования командой "упрямых" языков; это повышает согласованность, снижает неоднозначность и определённо снижает шанс выстрелить себе в ногу.


Сейчас мы раздаём статику через CloudFlare, хотя мы пропускаем 5% трафика через Fastly и 5% через CloudFront, чтобы держать их кеши прогретыми на случай если нам понадобится переключиться в экстренной ситуации. Недавно мы включили CloudFlare и для трафика приложений — в первую очередь для защиты от DDOS, но нас также порадовало повышение производительности.


Мы используем nginx и HAProxy совместно в качестве обратных прокси и балансировщиков нагрузки, чтобы перекрыть всю дианаграмму Венна наших потребностей.


Мы по-прежнему используем DataDog для мониторинга и PagerDuty для уведомлений, но теперь мы интенсивно используем ELK (Elasticsearch, Logstash, Kibana) для отладки возникших на продакшене проблем.


Базы данных


DynamoDB по-прежнему является нашей основной базой данных, однако это не было спокойным плаванием. Одной из извечных проблем, с которыми мы сталкнулись были ["горячие ключи"] (https://medium.com/medium-eng/how-medium-detects-hotspots-in-dynamodb-using-elasticsearch-logstash-and-kibana-aaa3d6632cfd) (Прим. переводчика: имеются ввиду ключи по которым выболняется большое количество запросов за небольшой промежуток времени), во время событий, ставших вирусными или при отправке постов миллионам подписчиков. У нас есть кластер Redis, который мы используем в качестве кеша перед Dynamo, что решает проблему для операций чтения. Оптимизации с целью повысить удобство разработчиков часто расходятся с таковыми для повышения стабильности, но мы работаем над этим.


Мы начали использовать Amazon Aurora для хранения новых данных из-за того, что она позволяет более гибко, чем Dynamo, извлекать и фильтровать данные.


Для хранения отношений между сущностями, которые представляют сеть Medium мы используем Neo4j, у нас запущен один мастер и две реплики. Люди, посты, теги и коллекции — это узлы графа. Рёбра создаются при создании сущности и при выполнении действий, таких как подписки, рекомендации или подсвечивания. Мы обходим граф чтобы фильтровать и рекомендовать посты.


Платформа данных


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


В качестве хранилища данных мы используем Amazon Redshift, он предосталвяет масштабируемую систему хранения и обработки данных, поверх которой строятся наши приложения. Мы постоянно импортируем наши основные данные (пользователи, посты) из Dynamo в Redshift и логи событий (просмотр поста, прокручивание страницы с постом) из S3 в Redshift.


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


Хотя SQL-запросы в Redshift нас устраивают, нам требуется загружать и выгружать данные в и из Redshift. Для ETL мы всё чаще обращаемся к Amazon Spark благодаря его гибкости и возможности масштабироваться в соотстветствии с нашими нужнами. С течением времени Spark скорее всего станет основным компонентом нашего конвейера данных.


Мы используем Protocol Buffers для схем данных (и правил изменения схем) чтобы держать все слои распределённой системы синхронизированными, включая мобильные приложения, веб-сервисы и хранилище данных. Мы аннотируем схемы деталями конфигурации, такими как имена талиц и индексов, правилами валидации записей, такими как максимальная длина строк или допустимые диапазоны значений чисел.


Людям хотятбыть на связи, так что разработчики мобильных и веб приложений могут подключатсья к нам единым образом, а исследователи данных (Data Scientists) могут интерпретировать поля единым образом. Мы помогаем нашим людям работать с данными, рассматривая схемы в качестве спецификаций, тщательно документируя сообщения и поля, и публикуя документацию, сгенерированную на основе .proto файлов.


Изображения


Наш сервер обработки изображений написан на Go и использует стратегию "водопад" (waterfall strategy) для отдачи обработанных изображений. Серверы используют groupcache, который предоставляет альтернативу memcache и позволяет снизить объём выполняемой дважды работы. Кеш в памяти резервируется постоянным кешем в S3; после чего изображения обрабатываются по запросу. Это даёт нашим дизайнероам свободу изменять способы отображения изображений и оптимизировать для разных платформ без необходимости запускать большие пакетные задачи по генерированию масштабированных изображений.


Хотя в настоящий момент сервер обработки изображений, в основном, используется для масштабирования и обрезки изображений, ранние версии сайта поддерживали светофильтры, размытие и другие эффекты. Обработка анимированных GIF-файлов доставляла огромную боль, но это тема отдельного поста.


TextShots


Прикольная функция TextShorts реализуется небольшим сервером на Go, использующим PhantomJS в качестве рендерера.


TextShots


Мне всё время хотелось поменять движок рендеринга на что-нибудь вроде Pango, но на практике возможность выводить изображения в HTML гораздо более гибкая и удобная. А частота, с которой эта функция используется позволяет там довольно легко справляться с наргузкой.


Произвольные домены


Мы позволяем людям настроить произвольные домены для их публикаций на Medium. Мы хотели, чтобы можно было авторизовываться один раз и для всех доменов, а также HTTPS для всех, так что заставить это всё работать было не тривиально. У нас есть набор серверов HAProxy, которые управляют сертификатами и направляют трафик на основные серверы. При настройке домена по-прежнему требуется ручная работа, но мы автоматизировали большую часть, за счёт интеграции с Namecheap. Сертификаты и связывание с публикациями выполняется выделенными серверами.


Веб фронтенд


В веб части мы предпочитаем оставаться близко к железу. У нас есть собственный фреймворк для одностраничных приложений (SPA), который использует Closure в качестве стандартной библиотеки. Мы используем Closure Templates для рендеринга как на стороне клиента, так и на стороне сервера, и Closure Compiler для минификации кода и разделения на модули. Редактор — это самая сложная часть нашего приложения, Ник о нём уже писал.


iOS


Оба наших приложения нативные, и минимально используют web view.


На iOS мы используем смесь и самодельных фреймворком и встроенных компонентов. На сетевом уровне мы используем NSURLSession для выполнения запросов и Mantle для парсинга JSON и восстановления моделей. У нас есть уровень кеширования, построенный поверх NSKeyedArchiver. Мы используем общий механизм рендеринга элементов списка с поддержкой произвольных стилей, что позволяет нам легко создавать новые списки с различными типами содержимого. Просмотр поста выполнен с помощью UICollectionView с собственным внешним видом (layout). Мы используем общие компоненты для рендеринга всего поста целиком и предпросмотра поста.


Мы собираем каждый коммит и распространяем сборку среди наших сотрудников, так что мы можем опробовать приложение так быстро, настолько это возможно. Частота выкладок приложения в AppStore привязана к циклу проверки приложений, но мы стараемся публиковать приложения даже если изменения минимальны.


Для тестов используем XCTest и OCMock.


Android


На Android мы используем последнюю версию SDK и вспомогательных библиотек. Мы не используем всеохватывающие фреймворки, предпочитая вместо этого использовать стандартные подходы к решению распространённых проблем. Мы используем guava для всего, чего не хватает в Java. В то же время, мы предпочитаем использовать сторонние средства для решения более узких задач. Мы используем свой API на основе protocol buffers, а затем генерируем объекты, которые используем в приложении.


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


Каждый коммит автоматически выкладывается в Play Store как алфа-версия и разаётся нашим сотрудникам. (Это относится и к приложению для нашего внутреннего варианта Medium — Hatch). Как правило, по пятницам мы пердаём альфа версию нашим бета-тестерам и даём им поиграться с нею на выходных, после чего, в понедельник, выкладываем для всех. Так как последняя версия кода всегда готова к релизу, то если мы находим серьезный баг, то мы сразу же публикуем исправление. Если мы беспокоимся о новой функции, то даём бета-тестерам поиграть с ней подольше; если нам всё нравится, то релизим ещё чаще.


A|B тестирование & Feature Flags


Все наши клиенты использую флаги функций (feature flags), выданные сервером и называемые variants. Они используются для A|B тестирования и отсключения не завершённых функций.


Разное


Есть ещё много всего вокруг продукта, о чём я не сказал: Algolia, которая позволяет быстрее работать с функциями поиска, SendGrid для входящей и исходящей электронной почты, Urban Airship для уведомлений, SQS для обработки очередей, Bloomd для фильтров Блума, PubSubHubbub и Superfeedr для RSS и так далее, и так далее.


Сборка, тестирование и выкладка


Мы используем непрерывную интеграцию и доставку (continuous integration and delivery), выкладывая как можно быстрее. Всем этим управляет Jenkins.


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


У нас есть как модульные тесты, так и функциональные тесты уровня HTTP. Все коммиты тестируются перед мерджем. Совместно с командой из Box мы работали над использованием Cluster Runner, чтобы распределять тесты и делать тестирование быстрее, у него есть также интеграция с GitHub.


Мы выкладываем в тестовое окружение так быстро, как только можем — в настоящий момент каждые 15 минут, успешные редакции затем становятся кандидатами для выклдывания на продакшен. Основные серверы приложений обновляются примерно пять раз в день, в некоторых случаях до 10 раз.


Мы используем синие/зелёные сборки (blue/green builds). Перед выкладкой на продакшен мы направляем трафик на тестовый экземпляр и отслеживаем частоту ошибок прежде чем продолжать. Откатывание назад выполняется средствами DNS.


Что дальше?


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


Присоединяйтесь к нам


Обычно мы всегда заинтересованы в общении с целеустремлёнными инженерами, у которых есть опыт работы над приложениями для конечных потребителей. У нас нет требований касательно языков, которые вы знаете, так как мы считаем, что хорошие инженеры могут быстро разобраться в новой дисциплине, но вы должны быть любопытными, знающими, решительными и чуткими. За основу можно принять iOS, Android, Node или Go.


Мы также готовим команду Product Science, так что мы ищем людей с опытом построения конвейеров данных и больших аналитических систем.


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


Вы можете прочитать больше в Medium Engineering.


Блогадарности Nathaniel Felsen, Jamie Talbot, Nick Santos, Jon Crosby, Rudy Winnacker, Dan Benson, Jean Hsu, Jeff Lu, Daniel McCartney и Kate Lee.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332860/


Метки:  

Летайте самолетами UNIGINE: новый авиатренажер для МАКС-2017

Среда, 12 Июля 2017 г. 14:58 + в цитатник
Сразу о главном: в этом году мы впервые едем на МАКС-2017 как участники и покажем еще один авиатренажер на платформе UNIGINE 2 Sim. Будем там все дни с 18 по 23 июля с командой разработчиков, некоторых вы уже знаете по истории про бенчмарк Superposition. Так что если вы хотели поговорить с этими парнями из UNIGINE — вот вам повод. А заодно научитесь летать на самолете-амфибии Бе-200 и тушить горящую тайгу как пилоты МЧС. Так что две причины прийти на наш стенд у вас уже есть!



Подробности, хайрезы из нового проекта и секретные фотографии, как водится, под катом.

При чем тут тренажеры, вы же делаете 3D-движок?


На самом деле 3D-графикой для профессиональных тренажеров мы занимаемся почти 10 лет, для них даже существует отдельная версия движка — платформа UNIGINE 2 Sim. Помимо разработки платформы визуализации, мы сами делаем на ней проекты — например, тренажер вертолета Bell 206 (возили на I/ITSEC в 2013 году), или демо VR-тренажера для экипажей МКС, который показывали на SIGGRAPH 2015. И это только публичные собственные проекты, наши клиенты на UNIGINE 2 Sim сделали уже несколько десятков профессиональных тренажеров.



Тренажер Bell 206 Ranger образца 2013 года


Этот кадр из демо Port Angeles 2014 года. Тогда площадь террейна была 2500 квадратных километров. В авиатренажере для МАКС виртуальное пространство в 100 раз больше, 500х500 км.

Зачем тренажерам нужна реалистичная 3D-графика?


Новое поколение пилотов выросло на современных играх, они привыкли видеть реалистичную картинку и ждут того же от авиатренажеров. Чем лучше графика — тем глубже погружение и тем качественнее подготовка пилотов, у которых ошибка иногда стоит сотен человеческих жизней. Реалистичная визуализация помогает отточить каждое действие до рефлексов и отработать все варианты внештатных ситуаций, когда из-за отказа авионики пилоту приходится полагаться только на зрительные ориентиры.

Большинство существующих профессиональных систем визуализации не блещет качественной 3D-графикой — именно это мы и стараемся изменить.




Тезис про реализм отлично доказывает это видео. Для того, чтобы сделать такие объемные многослойные 3D-облака, перед релизом UNIGINE 2.4 программистов движка «забрасывали» в небо на разных самолетах, чтобы лучше изучить, как ведут себя настоящие облака.



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

Где и на чем будем летать?


На стадии концепции у авиатренажера были разные варианты сценария, так что этот пост вполне мог бы быть о войне с сомалийскими пиратами. Но в итоге мы выбрали более миролюбивый вариант: тушение лесных пожаров в районе Байкала с самолета-амфибии Бе-200. Все же мы сами из Сибири, тема в прямом смысле близкая. И злободневная: по поводу горящей тайги этим летом бьют тревогу даже в NASA, называя сибирские пожары самыми масштабными за 10 000 лет.


Всего две недели назад Сибирь пылала от Красноярского края до Дальнего востока. Если вы сейчас перейдете по ссылке на карту fires.ru, увидите насколько их стало меньше. Самые сложные из них удалось потушить только с помощью авиации: самолетов Бе-200 и вертолетов Ми-8.

Работа Авиалесоохраны вообще пропитана суровой романтикой настоящих мужчин — про это есть отличная статья в National Geographic, например. Прыгать с парашютом на бушующий лесной пожар — это точно не для слабых духом. Поэтому мы сделали тренажер, который в перспективе поможет пилотам МЧС лучше подготовиться к тяжелой работе.


В главной роли — Бе-200, уникальный российский самолет-амфибия. Он может на лету забирать воду из озер и рек, нести до 12 тонн воды, с помощью тепловизионной камеры находить очаги пожаров на расстоянии в десятки километров, даже если огонь скрыт завесой дыма.

Из чего сделан тренажер?


Стенд для МАКС-2017 получился намеренно минималистичным — рама, кресло, симмерские контроллеры, компьютеры и UNIGINE 2 Sim inside. Раму делали под заказ по нашим чертежам. Начинка собрана из четырех компьютеров: три генератора изображений рисуют вид из кабины, один CIGI-сервер обеспечивает аэродинамику и имитирует приборную панель с тепловизором. Да, у тренажера пока нет точной реплики кокпита Бе-200 и подвижной платформы, хотя предложения от партнеров по созданию аппаратной части уже имеются. Но чтобы продемонстрировать возможности движка и погрузиться в полет на UNIGINE 2 Sim этого железа достаточно.


Внутри системников — i5 4570 / 16 GB / GTX 1080 / SSD.

UNIGINE 2 Sim поддерживает режим многоканальной визуализации с частотой 60 Гц. Для синхронизации между хостом и тремя IG мы используем протокол CIGI — широко распространенный в индустрии профессиональных тренажеров стандарт, одним из его разработчиков является компания Boeing.


Вид из кабины выводится на мониторы 32" с разрешением 2560x1440. Управляется все с помощью РУД/РУС Thrustmaster Hotas Warthog и одноименных педалей.

И напоследок видео проекта на стадии «work in progress».




Тренажер будет дорабатываться, к концу года планируем вывести его на еще более высокий уровень.
Приходите посмотреть и попробовать, а заодно поговорить о 3D-графике как разработчики с разработчиками. МАКС в этом году проходит с 18 по 23 июля в подмосковном Жуковском. Наш стенд D9-7 в павильоне D9. Билеты на МАКС-2017 можно купить на сайте авиасалона.

Предварительную перепись желающих ведем в комментариях!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332910/


Метки:  

Решение promo task от BI.ZONE CTF

Среда, 12 Июля 2017 г. 14:31 + в цитатник


31 мая компания BI.ZONE анонсировала новость о «CTFzone presidential election». И уже 1-го июня на Хабрахабр появилась публикация BI.ZONE объявляет выборы президента CTFzone, в которой, как и в прошлом году, спрятана «пасхалка». Если присмотреться к тексту публикации, можно заметить, что в предложении «Всем удачи на выборах!» есть ссылка ведущая на веб-страницу содержащую QR-код:


QR сканер выдал сообщение:
try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r_try_h4rd3r

Первое, что пришло в голову найти пользователя «try_h4rd3r» в twitter. Вторая попытка «try hard» заключалась в том, что мы попробовали разбить большой оригинальный QR-код на несколько маленьких:

      

Но все попытки были тщетны. Было принято решение скачать файл promo_task.png и исследовать в Hexed.it Сильно привлекла внимание строка:



Таким образом, поменяв расширение на .7z, получаем архив, который можем открыть 32-x битным 7-Zip. Содержимое архива: zipbomb.exe — самая настоящая ZIP-бомба. «Перерезав красный провод» и поменяв расширение на .zip, получаем архив, в котором лежит файл "-" размером 38,1 ГБ (40 959 016 020 байт). Ясно было сразу, что такой файл не прочесть разом. Было принято решение написать простенький скрипт на языке Python, который бы читал его по частям.

def read_in_chunks(file_object, chunk_size=1024):
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data

Результатом работы было множество строк, содержащих нули, среди которых тяжело было найти что-то особенное. Тогда скрипт модифицировали простой проверкой:

def read_in_chunks(file_object, chunk_size=1024):
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data

f = open('-')
for piece in read_in_chunks(f):
    if piece.count("0") < 1023:
        print(piece)

Результат работы скрипта:


Видим строчку: @cc77af5382e431dc_bot и первым делом вбиваем имя бота в Telegram. Получаем инструкции бота и задачу:


Подобрав кодировку Base64, мы декодируем и получаем:



Далее отправив результат боту /solve <ответ> можно получить flag. Однако сегодня Вы получите только это: «Sorry all flags are gone».

Всем удачи на CTFzone presidential election!

Спасибо tugric tammio за командную работу над задачей. Если они вам ответят в комментариях — им можно верить.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332930/


Метки:  

[Из песочницы] Реализация Elliptic curve Menezes-Vanstone cryptosystem на базе OpenSSL API

Среда, 12 Июля 2017 г. 14:29 + в цитатник
Здравствуйте, уважаемые хабровчане! По мере моего посильного занятия криптографией для своих скромных нужд, в попытках поддержать достойный уровень безопасности данных (я ориентируюсь на уровни, указанные в разделе ecrypt тут) меня начало беспокоить падение производительности при использовании криптоалгоритма RSA.

К сожалению, этот алгоритм оказался единственным в openssl, который допускает шифрование/дешифровку маленьких блоков данных (предполагается по смыслу статьи — ключей для алгоритмов симметричного шифрования) с помощью асимметричных криптоалгоритмов.

Прогулявшись по просторам интернета, удалось выяснить, что:

1. El-Gamal может успешно шифровать/расшифровывать, но эти операции не реализованы в openssl (есть реализация в libgcrypt). В плане быстродействия El-Gamal раза в 3 быстрее RSA
при той-же длине ключа и той-же криптостойкости на 1 бит ключа.

2. Elliptic Curve cryptosystem (ECC) приятно удивили скоростью и криптостойкостью на 1 бит ключа, но операции шифрования/дешифрования на основе ECC не реализованы в openssl.
Реализация ECC шифрования в libgcrypt есть, но очень специфична. Если коротко, то шифруемое сообщение m отображается на точку эллиптической кривой mG, из которой исходное сообщение m не может быть получено иначе, как взломом ECC или перебором всех возможных значений m.

3. В литературе [1] описана Menezes-Vanstone ECC, но есть уведомления о ее «уязвимости» [2]
Разберем этот вопрос подробнее.

Немного математики:


Для простоты будем говорить только о эллиптических кривых, вид которых описывается уравнением Вейершрасса $inline$y^2=x^3+ax+b$inline$ над полями целых чисел, определенных как Zp, где Zp — множество целых чисел, меньших некоторого простого числа р и больших нуля.

Тогда E(p,a,b) — где a,b принадлежат Zp — эллиптическая кривая над полем Zp, определяемая простым числом p и числами a,b. Далее нужно определить абстрактный нулевой элемент O (если коэффициент уравнения Вейершрасса b не равен 0, то за условную точку O можно принять координаты x=0,y=0 даже если эта точка и не является решением уравнения) и операцию сложения элементов кривой(точек), которая даст новую точку, принадлежащую этой-же кривой.

Естественно, должно получиться, что P+Q=Q+P, (P+Q)+R = P+(Q+R), P+O=O+P=P и если есть P(x,y), то есть -P=(x,-y) и P+(-P)=P-P=O.

Это — все математические операции, которые определены для группы точек эллиптической кривой.
В литературе [1] можно найти математические подробности, как определяются эти операции.

Складывать можно разные точки (P=G+Q) или точку с собой (P=Q+Q). То, что мы будем говорить о «умножении» — лишь способ сократить запись, и не писать P=Q+Q+Q+Q+...+Q m раз, а просто написать, что P=mQ. На самом деле нет операции «умножение» и, соответственно «деление», также как нет «возведения в степень» и «взятия логарифма».

Эта терминология часто используется, но для эллиптических кривых обозначает она не то, что обычно под этим понимается. Суммирование точки на эллиптической кривой с собой m раз можно назвать «умножением на m» или даже «возведением в степень m». Суть от этого не меняется, а поскольку обратной операции, «деления» или «взятия логарифма» не существует, получить m из точки m*G, даже зная G невозможно, говорят о «проблеме дискретного логарифма на эллиптических кривых». Такова устоявшаяся терминология.

На этой кривой выбирается (произвольная) точка G(Gx,Gy) которая является генератором группы точек, то есть, задавая разные m, получаем результат умножения mG, который образует циклическую группу точек (поскольку мы в конечном поле Zp). Размер этой циклической группы называется порядком (order) точки генератора G.

Таким образом, эллиптическая группа полностью описывается параметрами кривой E(p,a,b), точкой генератора G(Gx,Gy), и порядком группы ord, при этом ord*G=O. Это все называется параметрами эллиптической кривой, которые обычно являются общеизвестными, и идентифицируется по именам, например secp192k1 или prime256v1.

Приватным ключом пользователя является (секретное, случайное) число 1 < d < ord-1
Публичным ключом пользователя является точка Q, которая является произведением приватного ключа d и генератора группы G, Q=dG.

Что предлагает Elliptic curve Menezes-Vanstone cryptosystem [1] (MVC)?



Сторона отправителя:

1. Шифруемое сообщение m разбивается на две части x1 и x2, каждая из которых должна быть элементом поля Zp, для этого достаточно проверить их длину и сравнить с длиной параметра кривой p.
2. Отправитель выбирает (секретное, случайное) число 1 < ks < ord-1.
3. Отправитель умножает точку генератора G на число ks, y0=ks*G
4. Отправитель вычисляет точку Z(Zx,Zy), умножая публичный ключ получателя Q на число ks, Z=ks*Q
5. Отправитель вычисляет y1=x1*Zx(mod p), y2=x2*Zy(mod p)

Вычислительные затраты отправителя: генерация случайного числа нужной длины, 2 операции умножения точки на число, 2 операции умножения по модулю p.

Шифротекстом является точка y0, число y1, число y2. Точка содержит 2 числа — координаты x,y. Общий объем шифротекста приблизительно равен 4*p, для ключа ECC длиной 192 бита (24 байта) приблизительно 24*4=96 байт.

Сторона получателя:

1. Получатель проверяет принадлежность точки y0 кривой, заданной параметрами E(p,a,b),G,ord.
2. Получатель вычисляет точку Z, умножая шифротекст y0 на свой приватный ключ d, Z=d*y0=d*ks*G=ks*d*G=ks*Q.
3. Получатель вычисляет мультипликативную инверсию компонентов Z(Zx,Zy), e1=inv(Zx)(mod p), e2=inv(Zy)(mod p).
4. Получатель восстанавливает x1, x2: x1=y1*e1(mod p), x2=y2*e2(mod p).

Вычислительные затраты получателя: проверка принадлежности точки к кривой, 1 операция умножения точки на число, 2 операции вычисления мультипликативной инверсии по модулю p,
2 операции умножения по модулю p.

Уязвимость или слабость MVC


В 1997г Klaus Kiefer [2] показал, что MVC не является системой, использующей вероятностное шифрование, вопреки своему дизайну. Зная шифротекст, зная параметры кривой, можно провести «known plaintext attack» (атака с угадыванием открытого текста).

Как это выглядит:

Известны параметры кривой E(p,a,b),G,ord. Известен шифротекст y0,y1,y2. Мы предполагаем, что открытым текстом является x1,x2.

Если точка F(f1,f2) f1=y1*(inv(x1))(mod p), f2=y2*(inv(x2))(mod p) принадлежит кривой E(p,a,b), то с вероятностью ошибки 1/p x1,x2 действительно есть искомый открытый текст.

Что это означает на практике?

То, что возможно устроить перебор всех значений x1,x2 с вычислительными затратами 2 операции вычисления мультипликативной инверсии по модулю p, 2 операции умножения по модулю p на каждый вариант x1,x2 и с вероятностью ошибки 1/p найти открытый текст, соответствующий зашифрованному. Отрицательный результат проверки всегда верен, положительный может содержать ошибочное распознавание с вероятностью 1/p. Операция перебора x1,x2 хорошо распареллеливается, много процессов могут независимо перебирать свои неперекрывающиеся диапазоны значений x1,x2.

Для справки: я бы хотел посмотреть на перебор (просто перебор, без вычислений) всех возможных значений ключа 192 или 256 бит. Да, даже и 128-168 бит.

Понятно, что перебором можно вскрыть только небольшие кусочки зашифрованных данных, до 48-64 бит. И перебор этот можно устроить без MVC, для задачи найти ключ перебором всех возможных значений использование MVC не нужно, это лишняя сущность.

Что мы имеем в итоге?


Если шифровать достаточно большие (128 бит и более) тексты, исключающие возможность за разумное время и приемлемые затраты найти их перебором, данная «уязвимость» не играет никакой роли.

На сегодняшний день минимальная рекомендуемая длина ключа ECC — 192 бита (24 байта). Длина шифруемых данных MVC в этом случае не должна превышать 2*24=48 байт, а самый стойкий ключ AES или GOST, имеет длину 256 бит (32 байта).

Зато мы получаем криптостойкий и достаточно быстрый (по моим прикидкам, ECC-224 в 5 раз быстрее RSA-2048, в 3 раза быстрее ElGamal-2048) алгоритм асимметричного шифрования.
Я считаю, что Elliptic curve Menezes-Vanstone cryptosystem совершенно незаслуженно забыта.
Пытаясь восполнить этот пробел, выкладываю исходники на языке C с использованием openssl API.

Литература:
1. COMPUTER SECURITY AND CRYPTOGRAPHY, ALAN G. KONHEIM, Published by John Wiley & Sons, Inc., Hoboken, New Jersey, 2007. ISBN-13: 978-0-471-94783-7 ISBN-10: 0-471-94783-0
2. «A Weakness of Menezes-Vanstone Cryptosystem», Klaus Kiefer, member of research group of prof. J. Buchmann, 1997

//
// DESCRIPTION     'EC Menezes-Vanstone cryptosystem functions openssl/Linux'
// COMPILER        'gcc (GCC) 4.8.2'
// FILE            'ecmv.h'
// AUTHOR          "Nick Korepanov"
// Linux-3.10.104, glibc-2.17, OpenSSL 1.0.1u
// ECC-192/224/256

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Author of this program can be contacted by electronic mail
// korepanovnd@gmail.com
// Copyright (c) 2017 Nick Korepanov. All rights reserved.

// This product includes software developed by the OpenSSL Project
//     for use in the OpenSSL Toolkit. (http://www.openssl.org/)

// COMPUTER SECURITY AND CRYPTOGRAPHY, ALAN G. KONHEIM
// Published by John Wiley & Sons, Inc., Hoboken, New Jersey, 2007
// Library of Congress Cataloging-in-Publication Data:
// Konheim, Alan G., 1934–
// Computer security & cryptography / by Alan G. Konheim.
// p. cm.
// Includes bibliographical references and index.
// ISBN-13: 978-0-471-94783-7
// ISBN-10: 0-471-94783-0
// 1. Computer security. 2. Cryptography. I. Title.
// QA76.9.A25K638 2007
// 005.8--dc22 2006049338

// 15.9 THE MENEZES–VANSTONE ELLIPTIC CURVE CRYPTOSYSTEM, p. 443

// Another very significant source - "A Weakness of Menezes-Vanstone Cryptosystem", Klaus Kiefer, member of research group of prof. J. Buchmann, 1997
// Shortly, this work show ability of "known plain text attack (KPTA)", with probability O(1/p) of false detection. 
// What does it mean? If we encrypt 128-bit session key, for success KPTA we must search in 2^128 combinations of session key ...   

// Known plaintext attack for EC MV cryptosystem
// Curve E(p,a,b) known from public key, 
// y0, y1, y2 - ciphertext  
// random select 1 < x1 < p and 1< x2 < p
// calculate inversion a=inv(x1)(mod p), b=inv(x2)(mod p)
// c1=a*y1(mod p), c2=b*y2(mod p)
// if C(c1,c2) is point of curve E, x1 and x2 is plaintext with error probability O(1/p)
// z=((c1)^3 + a*c1 + b)(mod p)
// if (z^((p-1)/2))(mod p) == 1 there are 2 points (c1,+-c2) in curve  E(p,a,b)

//#include 
#include 
#include 
//#include 

#include rand.h>
#define OPENSSL_NO_EC2M
#include ec.h>

#define FORMATBIN 1
#define FORMATHEX 0

struct BinFmt192	// binary format of ECMV encrypted block, EC key = 192 bits
{
	unsigned char y0[1+192/4]; 	// 2*24 byte BIGNUM + 1 header byte
	unsigned char z1;
	unsigned char y1[192/8];	// 24 bytes BIGNUM
	unsigned char z2;
	unsigned char y2[192/8];	// 24 bytes BIGNUM
	unsigned char z3;
};	// size = 100 bytes

struct BinFmt224	// binary format of ECMV encrypted block, EC key = 224 bits
{
	unsigned char y0[1+224/4]; 	// 2*28 byte BIGNUM + 1 header byte
	unsigned char z1;
	unsigned char y1[224/8];	// 28 bytes BIGNUM
	unsigned char z2;
	unsigned char y2[224/8];	// 28 bytes BIGNUM
	unsigned char z3;
};	// size = 116 bytes

struct BinFmt256	// binary format of ECMV encrypted block, EC key = 256 bits
{
	unsigned char y0[1+256/4]; 	// 2*32 byte BIGNUM + 1 header byte
	unsigned char z1;
	unsigned char y1[256/8];	// 32 bytes BIGNUM
	unsigned char z2;
	unsigned char y2[256/8];	// 32 bytes BIGNUM
	unsigned char z3;
};	// size = 132 bytes

// Encrypt plaintext of length len with public EC key pubkey
// and store ciphertext in chipher = y0 (point), y1 (bignum), y2 (bignum)
// return error code, 0 if all OK
// Error codes: 
/*
 * 1 	// no curve in key?
 * 2	// wrong plaintext has odd length
 * 3	// plaintext too long for this key
 * 8	// binary format of encrypted block not defined for this key length
 * 4	// internal error: ks is wrong, error in do-while
 * 5	// internal error: error in EC_POINT_mul y0=ks*g
 * 6	// internal error: error in EC_POINT_mul z=ks*q
 * 7	// internal error: error EC_POINT_get_affine_coordinates_GFp
 * errors 4,5,6,7 lead to memory leak :(
 * */
// hex format:
// Encrypted text consist of 3 hex strings, each is ending with '\n'=0x0A
// First string has '04' header and two times longer than second and third
// length of encrypted block = 2*4*bits/8 + 5 = bits + 5 bytes, where 'bits' is length of EC key in bits
// Plaintext data MUST be of even length in bytes and not longer than 2*bits/8 = bits/4 bytes 
// bin format:
// Encrypted text is binary block consist of 3 binary elements, each is ending with NULL=0x00 byte
// First element has 0x04 header and two times longer than second and third
// length of encrypted block in binary form = 4*bits/8 + 4 = bits/2 + 4 bytes, where 'bits' is length of EC key in bits
// In this example I used 192,224,256 bit EC keys and binary form for other key length don't supported :( 
int EC_MV_pubkey_encrypt(unsigned char *cipher, EC_KEY* pubkey, unsigned char* plaintext, size_t len, int format);

// Decrypt with private EC key privkey ciphertext in  cipher = y0 (point), y1 (bignum), y2 (bignum) 
// and store result in plaintext  
// return error code, 0 if all OK 
/* Error codes:
 * 1 	// no curve in key?
 * 8	// unknown format of ECMV encrypted block
 * 9	// binary format of encrypted block not defined for this key length
 * 10	// wrong format of binary encrypted block
 * 2	// invalid hex point y0 representation
 * 3,4	// wrong format of HEX encrypted data
 * 11	// point y0 is not on curve
 * 6	// internal error: error in EC_POINT_mul z=ks*q
 * 7	// internal error: error EC_POINT_get_affine_coordinates_GFp
 * errors 2,3,4,11,6,7 lead to memory leak :(
 * */
int EC_MV_privkey_decrypt(unsigned char*  cipher, EC_KEY *privkey, unsigned char* plaintext);


//
// DESCRIPTION     'EC Menezes-Vanstone cryptosystem functions openssl/Linux'
// COMPILER        'gcc (GCC) 4.8.2'
// FILE            'ecmv.c'
// AUTHOR          "Nick Korepanov"
// Linux-3.10.104, glibc-2.17, OpenSSL 1.0.1u
// ECC-192/224/256

//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program; if not, write to the Free Software
//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Author of this program can be contacted by electronic mail
// korepanovnd@gmail.com
// Copyright (c) 2017 Nick Korepanov. All rights reserved.

// This product includes software developed by the OpenSSL Project
//     for use in the OpenSSL Toolkit. (http://www.openssl.org/)

#include "ecmv.h"

int EC_MV_pubkey_encrypt(unsigned char *cipher, EC_KEY* pubkey, unsigned char* plaintext, size_t len, int format)
{
const EC_GROUP *curve;	// curve, q and g are part of pubkey, it was allocated and free with pubkey 
const EC_POINT *q; 
EC_POINT *y0, *z;
BIGNUM  *p, *a, *b, *ks, *o1, *z1, *z2, *y1, *y2, *x1, *x2, *ord; 
int bits, i=0, err;
unsigned char buffer[250];
//size_t length;
BN_CTX *ctx;

curve=EC_KEY_get0_group(pubkey);
if(curve)
	bits = EC_GROUP_get_degree(curve);
else
	return 1; 	// no curve in key?

if(len%2)
	return 2;	// wrong plaintext has odd length
	
if(len > 2*bits/8)
	return 3;	// plaintext too long for this key

if( !(bits == 192 || bits == 224 || bits == 256) && format)
	return 8;	// binary format of encrypted block not defined for this key length
	
//prepare bignums
p=BN_new(); a=BN_new(); b=BN_new(); ks=BN_new(); o1=BN_new(); z1=BN_new(); z2=BN_new(); y1=BN_new(); y2=BN_new(); x1=BN_new(); x2=BN_new(); ord=BN_new(); 
ctx=BN_CTX_new();

//prepare points
//q=EC_POINT_new(curve); g=EC_POINT_new(curve); 
y0=EC_POINT_new(curve); z=EC_POINT_new(curve);

// split plaintext at two parts, and assign it to BIGNUMs
BN_bin2bn(plaintext, len/2, x1);
BN_bin2bn(plaintext+len/2, len/2, x2);
	
// get public key q	
q=EC_KEY_get0_public_key(pubkey);
// get generator point g
//g=EC_GROUP_get0_generator(curve);
// get order of g
EC_GROUP_get_order(curve, ord, ctx );	

// get prime p
EC_GROUP_get_curve_GFp(curve, p, a, b, ctx );

BN_sub(o1, ord, BN_value_one());	// o1=ord-1

do
	{
	if( i>= 10)
		break;
	// make secret session key ks > 1 and ks < (o-1)
	RAND_bytes(buffer, bits/8);
	BN_bin2bn(buffer, bits/8, ks);
	i++;
	}
while( BN_cmp(BN_value_one(), ks) >=0 || BN_cmp(o1,ks) <=0 );

if(i>=10)
	return 4;	// ks is wrong, error in do-while

// y0=ks*g
err=EC_POINT_mul(curve, y0, ks, NULL, NULL, ctx );
if(err == 0)
	return 5;	// error in EC_POINT_mul y0=ks*g

// z=ks*q
err=EC_POINT_mul(curve, z, NULL, q, ks, ctx );
if(err == 0)
	return 6;	// error in EC_POINT_mul z=ks*q

// get z1,z2 = Z(z1,z2)
err=EC_POINT_get_affine_coordinates_GFp(curve, z, z1, z2, ctx );
if(err == 0)
	return 7;	//error EC_POINT_get_affine_coordinates_GFp
	
//y1 = z1*x1(modulo p)
//y2 = z2*x2(modulo p)
BN_mod_mul(y1, z1, x1, p, ctx);
BN_mod_mul(y2, z2, x2, p, ctx);

/* if bits=192, 24 bytes per every BIGNUM, point contains 2 Bignum + 1 byte header */
if(format)
	{	// bin format
	if(bits == 192)
		{
		struct BinFmt192 *out;
		out=(struct BinFmt192 *)cipher;
		EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
		BN_bn2bin(y1, (unsigned char*)&out->y1);
		BN_bn2bin(y2, (unsigned char*)&out->y2);
		out->z1=out->z2=out->z3=0;		
		}
	if(bits == 224)
		{
		struct BinFmt224 *out;
		out=(struct BinFmt224 *)cipher;
		EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
		BN_bn2bin(y1, (unsigned char*)&out->y1);
		BN_bn2bin(y2, (unsigned char*)&out->y2);
		out->z1=out->z2=out->z3=0;		
		}
	if(bits == 256)
		{
		struct BinFmt256 *out;
		out=(struct BinFmt256 *)cipher;
		EC_POINT_point2oct(curve, y0, POINT_CONVERSION_UNCOMPRESSED, (unsigned char*)&out->y0, sizeof(out->y0), ctx);
		BN_bn2bin(y1, (unsigned char*)&out->y1);
		BN_bn2bin(y2, (unsigned char*)&out->y2);
		out->z1=out->z2=out->z3=0;		
		}
	}
else
	{	// hex format
	strcpy((char*)cipher, EC_POINT_point2hex(curve, y0, POINT_CONVERSION_UNCOMPRESSED, ctx));
	strcat((char*)cipher, "\n");

	strcat((char*)cipher, BN_bn2hex(y1));
	strcat((char*)cipher, "\n");

	strcat((char*)cipher, BN_bn2hex(y2));
	strcat((char*)cipher, "\n");
	}

// free points
//EC_POINT_free(q); EC_POINT_free(g); 
EC_POINT_free(y0); EC_POINT_clear_free(z);

BN_CTX_free(ctx);

BN_clear(ks);
BN_clear(x1);
BN_clear(x2);
BN_clear(z1);
BN_clear(z2);

//free bignums
BN_free(p); BN_free(a); BN_free(b); BN_free(ks); BN_free(o1); BN_free(z1); BN_free(z2); BN_free(y1); BN_free(y2); BN_free(x1); BN_free(x2); BN_free(ord);
return 0;
}

int EC_MV_privkey_decrypt(unsigned char*  cipher, EC_KEY *privkey, unsigned char* plaintext)
{
const EC_GROUP *curve;	// curve, d are part of privkey, it was allocated and free with privkey 
const BIGNUM *d;
EC_POINT *y0, *z;
BIGNUM  *p, *a, *b, *z1, *z2, *y1, *y2, *x1, *x2; 
int err, bits, format;
unsigned char *ptr;
BN_CTX *ctx;

ctx=BN_CTX_new();

curve=EC_KEY_get0_group(privkey);
if(!curve)
	return 1; 	// no curve in key?

bits = EC_GROUP_get_degree(curve);

if( cipher[0] == 0x04 )
	format=FORMATBIN;
if(	cipher[0] == 0x30 )
	format=FORMATHEX;
if(cipher[0] != 0x04 && cipher[0] != 0x30)
	return 8;	// unknown format of ECMV encrypted block	

if( !(bits == 192 || bits == 224 || bits == 256) && format)	
	return 9; // binary format of encrypted block not defined for this key length

if(format && bits == 192 && (cipher[48+1] || cipher[48+1+24+1] || cipher[48+1+24+1+24+1] ))
	return 10;	//wrong format of binary encrypted block	
if(format && bits == 224 && (cipher[56+1] || cipher[56+1+28+1] || cipher[56+1+28+1+28+1] ))
	return 10;	//wrong format of binary encrypted block	
if(format && bits == 256 && (cipher[64+1] || cipher[64+1+32+1] || cipher[64+1+32+1+32+1] ))
	return 10;	//wrong format of binary encrypted block	

//prepare bignums
p=BN_new(); a=BN_new(); b=BN_new(); z1=BN_new(); z2=BN_new(); y1=BN_new(); y2=BN_new(); x1=BN_new(); x2=BN_new();  

//prepare points
y0=EC_POINT_new(curve); z=EC_POINT_new(curve);
	
// get private key d	
d=EC_KEY_get0_private_key(privkey);
// get prime p
EC_GROUP_get_curve_GFp(curve, p, a, b, ctx);

if(format)
	{
	if(bits == 192)
		{
		struct BinFmt192 *in;
		in=(struct BinFmt192 *)cipher;
		EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
		BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
		BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
		}
	if(bits == 224)
		{
		struct BinFmt224 *in;
		in=(struct BinFmt224 *)cipher;
		EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
		BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
		BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
		}
	if(bits == 256)
		{
		struct BinFmt256 *in;
		in=(struct BinFmt256 *)cipher;
		EC_POINT_oct2point(curve, y0, (const unsigned char *)&in->y0, sizeof(in->y0), ctx);
		BN_bin2bn((const unsigned char *)&in->y1, sizeof(in->y1), y1);
		BN_bin2bn((const unsigned char *)&in->y2, sizeof(in->y2), y2);
		}
	}
else
	{
	// read y0
	ptr=cipher;
	y0=EC_POINT_hex2point(curve, (const char *)ptr, y0, ctx);
	if(y0 == NULL)
		return 2;	//invalid hex point representation
	
	//read y1,y2
	ptr=strchr((const char *)ptr,'\n'); 
	if(ptr == NULL)
		return 3;	//wrong format of encrypted data
	ptr++;
	BN_hex2bn(&y1, (const char *)ptr);

	ptr=strchr((const char *)ptr,'\n'); 
	if(ptr == NULL)
		return 4;	//wrong format of encrypted data
	ptr++;
	BN_hex2bn(&y2, (const char *)ptr);
	}

if( !EC_POINT_is_on_curve(curve, (const EC_POINT *)y0, ctx) )
	return 11;	// point is not on curve

// z=d*y0=d*ks*g=ks*q
err=EC_POINT_mul(curve, z, NULL, y0, d, ctx );
if(err == 0)
	return 6;	// error in EC_POINT_mul z=ks*q

// get z1,z2 = Z(z1,z2)
err=EC_POINT_get_affine_coordinates_GFp(curve, z, z1, z2, ctx );
if(err == 0)
	return 7;	//error EC_POINT_get_affine_coordinates_GFp

// a=inv(z1)(mod p)
BN_mod_inverse(a, z1, p, ctx);	
// b=inv(z2)(mod p)
BN_mod_inverse(b, z2, p, ctx);	
	
//x1 = a*y1(modulo p)
//x2 = b*y2(modulo p)
BN_mod_mul(x1, a, y1, p, ctx);
BN_mod_mul(x2, b, y2, p, ctx);

// decode plaintext from two parts
BN_bn2bin(x1, plaintext);
BN_bn2bin(x2, plaintext+BN_num_bytes(x1));

// free points
EC_POINT_free(y0); EC_POINT_clear_free(z);

BN_CTX_free(ctx);

BN_clear(x1);
BN_clear(x2);
BN_clear(z1);
BN_clear(z2);
BN_clear(a);
BN_clear(b);

//free bignums
BN_free(p); BN_free(a); BN_free(b); BN_free(z1); BN_free(z2); BN_free(y1); BN_free(y2); BN_free(x1); BN_free(x2);
return 0;
}

Спасибо за внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332928/


Метки:  

Интервью с программистом из Тинькофф Банка Андреем Степановым о языке Python и ML

Среда, 12 Июля 2017 г. 14:23 + в цитатник
Серию интервью с докладчиками PyCon Russia продолжает разговор с разработчиком-аналитиком из Тинькофф Банка Андреем Степановым. Мы поговорили с Андреем о месте Python в инфраструктуре банка, о машинном обучении и о технологии распознавания речи.



— Андрей, как ты пришел к разработке на Python?

— В основном, как и многие в моей отрасли, через ML. Но это будет не совсем честный ответ. Питоном я увлекался до этого, можно даже сказать, со школы. А еще был классный курс в ШАДе. Поначалу меня пугал синтаксис, до этого я изучал лишь C-подобные языки, и после них казалось, что полагаться на пробелы и табы для структурирования программы не совсем разумно. Но потом привык, понравилась простота, а через некоторое время пришло понимание, какой же это на самом деле богатый и многогранный язык, какие сумасшедшие вещи можно с ним делать. Это не то, что приходится часто делать на работе, но приятно осознавать, что инструмент, с которым ты работаешь, достаточно гибок и мощен.

— У тебя интересная должность — разработчик-аналитик. Чем занимаются разработчики-аналитики? Над чем ты сейчас работаешь?

— Нас, разработчиков-аналитиков, в банке не очень много. Это что-то среднее между специалистом по работе с большими данными и промышленным разработчиком. Здесь надо и код уметь писать красивый и поддерживаемый, и с данными уметь работать, ML эксперименты проводить, анализировать результаты и соответствующим образом обновлять математические модели. Ну, или искать новые подходы к решению задачи, если текущие не работают. А если работают, и со стороны бизнеса все ок, то пробовать реализовать прототип для продакшена. Соответственно, стек технологий очень широкий. Работа интересная и довольно трудоемкая, очень много приходится читать доков про новые технологии и вообще в принципе следить за новостями в области ML и выбранных технологий для продакшен стека.

Сейчас я работаю на проекте по распознаванию речи для банка.

— Какие технологии использует Тинькофф? Много ли у вас ML? Какое место в инфраструктуре занимает Python?

— Из тех, что используем мы: Python, Tensorflow, Docker, Protocol Buffers, GRPC, Cython. У других команд, конечно, может быть другой стек технологий.

ML много, в продакшене особенно ценятся простые и быстрые, хорошо интерпретируемые ML модели. Сейчас развиваем и модный Deep Learning. Распознавание речи, диалоговые системы — все это как раз использует глубокие нейросети. Применение нейронных сетей может дать в будущем качественный скачок в создании клиентских продуктов и сервисов, поэтому мы уделяем им отдельное внимание.

Python в инфраструктуре наших ML решений пока занимает роль языка для проведения экспериментов и тренировки первоначальных моделей. В нём есть все для ML продакшена, и мы пытаемся исследовать эту тему. В идеале хотелось бы иметь инструмент, который позволит аналитикам экспортировать обученные модели и предоставлять быстрый внешний API для их расчета. Для Deep Learning что-то такое отчасти уже есть, я сейчас говорю про Tensorflow Serving. Было бы круто, если такой инструмент существовал для всех часто используемых ML моделей.




На прошлогоднем PyCon Martin Gorner из Google подробно рассказал про TensorFlow

— Насколько страшно писать код для банка?

— В RnD вообще не страшно, а должно быть? :) Ребятам, которые ближе к персональным данным и деньгам, наверное, страшнее, но у них там и внешнего контроля над процессом больше.

— Насколько я знаю, Тинькофф стал первым в России, кто внедрил у себя технологию распознавания клиентов по голосовому слепку. Это так? Ты принимал участие в разработке? Расскажи поподробнее про эту фичу?

— Если я правильно понял, вы говорите про совместный проект с NICE Systems. Мы внедрили распознавание по голосу в 2014 г, это произошло до того, как я пришел работать в Тинькофф.

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

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

— Стоит ли ждать, что голосовые помощники и голосовые технологии станут нормальными собеседниками?

— Со временем, думаю, да. Но сколько времени должно пройти для этого, никто не знает.

Сейчас всё, даже с учетом последних наработок в области GPGPU, упирается в вычислительные способности современного железа и разработку эффективных нейросетевых архитектур. Основная проблема заключается даже не в тренировке, хотя и в этой области развитие железа помогло бы, а в предоставлении миллионам пользователей вычислительного времени для обсчёта нейросети на их данных. Представленные недавно Tensor Processing Unit (TPU) от Google и специализированные вычислительные юниты для Deep Learning в GPU ядре новой архитектуры от NVIDIA позволяют частично решить эту проблему, но должно пройти еще много времени, чтобы эти технологии освоили разработчики и ученые.

Да, человечество сделало огромный шаг в области AI по сравнению с тем, что было, например, 10 лет назад, но до полноценного искусственного интеллекта, думаю, еще далеко.

— На твой взгляд, в каком направлении будут развиваться эти технологии в ближайшие годы?

— Думаю, в ближайшее время произойдёт серьезное усиление человеческих способностей с помощью AI в тех областях, где сейчас традиционно человек считается превосходящим компьютер (это как раз голосовые помощники, чатботы, диалоговые системы). Интерфейсы взаимодействия всё еще сохранят отчасти искусственный, компьютерный «облик», то есть естественно разговаривать с голосовым помощником на произвольную тему пока ещё не получится, но в рамках фиксированного домена разговор, даже естественный, может вполне состояться.

— Ты наверняка знаешь, что происходит в мире финтеха. Какова роль Python в этой отрасли?

— Python зарекомендовал себя как отличный язык для экспериментов с ML. В продакшене не все так просто, я сейчас в основном про скорость работы. Но computationally-intensive часть кода всегда можно реализовать на Cython или как C extension. Тогда проблем со скоростью быть не должно. У Python очень классное комьюнити, и возникшие потребности можно всегда решить внешними пакетами.

— Какие инструменты ты используешь для организации работы (в том числе для планирования времени, организации рабочего пространства и т.п.)?

— Я предпочитаю почти пустой рабочий стол с минимальным количеством самого необходимого. Для планирования использую листок бумаги и голову, иногда Google Keep для долгосрочных задач.

— Ты читаешь какой-нибудь профессиональный блог? Какие информационные ресурсы ты мог бы порекомендовать коллегам для развития скиллов?

— Если про ML и Deep Learning, то мне нравятся блоги Andrej Karpathy и WildML. Они, правда, уже давно не обновлялись. Еще есть классный блог про Tensorflow от одного француза. Также мне очень понравилась видеолекция от David Beazley про метапрограммирование в Python 3. А еще у Python-а и стандартной библиотеки очень классные доки, я всегда нахожу в них что-то новое.

17 июля на PyConRu Андрей проведет большой мастер-класс «Распознавание речи на Python без PhD» на котором расскажет о том, как написать и натренировать свой простой движок для распознавания речи с Tensorflow и нейросетями в максимально сжатые сроки.



Спасибо нашим спонсорам, которые делают конференцию возможной: золотому спонсору — компании Adcombo, партнеру энергии и хорошего настроения компании ЦИАН, серебряным спонсорам — Rambler&Co и ДомКлик, бронзовому спонсору — MediaScope. Спасибо за поддержку Python Software Foundation.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332870/


Метки:  

Константин Будник, EPAM: “Apache Hadoop перешел в фазу commodity — там почти не появляется ничего нового.”

Среда, 12 Июля 2017 г. 13:41 + в цитатник
В начале ноября в Киеве уже в шестой раз пройдёт одна из ключевых в Восточной Европе Java-конференций JavaDay 2017. Хотя до события еще достаточно времени, мы предметно пообщались с одним из спикером конференции — Константином Будником, Chief BigData Technologist и Open Source Fellow EPAM Systems — о силе open-source, Big Data и будущем Hadoop.




Вы почти 15 лет работали в Sun Microsystems, потом долгое время работали над Hadoop. Как развивалась ваша карьера?


Я работал в Sun начиная с 1994 года, с момента открытия офиса в Санкт-Петербурге — был сотрудником номер 6. Там я проработал 15 лет и занимался всем — от компиляторов до распределенных и кластерных системам. Я работал над операционной системой, над разными частями Java stack — в частности, в команде JVM, участвовал в разработке виртуальной машины, создал несколько фреймворков для разработчиков JVM.

Если рассматривать open-source, то я работал с открытыми технологиями примерно с 1994 года. В частности в середине 2000-х я помогал добавить Java в Linux Software Foundation, который создали незадолго до этого. Java стала частью LSB – Linux Standard Base. Тогда же немного поработал с Яном Мердоком — создателем Linux-дистрибутива Debian.

Начиная с 2000-х я начал заниматься распределенными системами, и за время работы в Sun я получил порядка 15 американских патентов в области распределенных вычислений и технологий.

В 2009 году я перешел в Yahoo и начал работать над Apache Hadoop — писал код, который шел в проект Apache. За первые 3 года работы в проекте Hadoop, я написал больше кода, чем, на тот момент, было у компании IBM. Конкретнее, я работал над HDFS – дистрибутивной файловой системой, для хранения данных в Hadoop, и системой распределенного fault-injection. После этого прошло некоторое время, и вот уже год я сотрудничаю с EPAM.

Над чем вы там работаете?


В EPAM есть департамент Big Data Practice. На сегодня он насчитывает около 300 экспертов в области обработки «больших данных». Часть из них занимается data science, еще часть — построением архитектуры для обработки больших объемов данных. Я Chief Technologist этого подразделения, и помимо этого, веду направление open-source software development для всей компании. ЕРАМ активно привлекает инженеров для поддержки open-source проектов на базе открытых систем и платформ, с помощью которых решает задачи по разработке продуктов.
 
Примечательно, что доля открытого ПО в Big Data составляет около 94-95%. «Закрытого» ПО коммерческого происхождения, которое используется для решения задач обработки «больших данных», очень мало.

Почему так?


У бизнесов появляется все больше уверенности в том, что коммерческие производители ПО могут — и делают — их заложниками своих решений. Инвестиции в одну конкретную технологию приводят потере гибкости всей технологической базы. Решение проблемы за счет привлечения дополнительных технологий других производителей приводит к несовместимости интерфейсов, форматов хранения данных, языков реализации и другим «симптомам».

Помимо этого, попадая в зависимость от одного производителя ПО, компания делает его «внутренним монополистом». Вендор, не советуясь с клиентом, поднимает цену лицензий или произвольно меняет набор технологических возможностей программного продукта. Чтобы избавиться от такого vendor lock-in нужны новые инвестиции: в смену оборудования, ПО, ротацию сотрудников.

Использование open-source решений гарантирует отсутствие таких проблем. Если вы работаете с Linux и Red Hat и эти решения вас больше не устраивают, вы можете перейти на открытые и бесплатные CentOS, Fedora, Debian, или купить поддержку Canonical для Ubuntu. С точки зрения бизнеса, особых изменений не произойдет. Когда речь идет об обработке критических для бизнеса объемов данных, уход от vendor lock-in превращается в насущную необходимость.

Вы уже двадцать лет в open-source сообществе. За это время оно изменило мир благодаря Linux и Android. Помимо этого, технологии open-source стали базой для разработки продуктов крупных компаний. В чем сила открытого сообщества, что его двигает вперед? Как крупным компаниям правильно строить взаимодействие с такими сообществами?


Идея довольно проста: людям интересно работать над тем, над чем им интересно работать. Это гениальный в своей простоте принцип. Open-source позволяет людям, даже если на основной работе они занимаются чем-то скучным, выразить себя. Человек приходит в такое сообщество и находит единомышленников, получает возможность сделать что-то уникальное — например, изменить направление разработки всего проекта в ту сторону, которую он считает нужной. Это очень мощный стимул для людей креативных и технологических.

Проследите за Линусом Торвальдсом: человек действительно поменял мир, потому что он умен, настойчив, продуктивен, увлекет людей своим примером и компетенциями. Этим open-source выгодно отличается от некоторых коммерческих разработок: здесь ценится что и как ты делаешь, а не как много и красиво ты говоришь и обещаешь. Такой принцип называется meritocracy и он — отличная альтернатива традиционной структуре управления. Его активно использует GitHub и ряд других компаний.

Если посмотреть на принципиальную разницу между коммерческой разработкой и open-source, то первая всегда основывается на том, ЧТО клиент будет покупать. Бизнес должен быть прибыльным. И если ты не политик или CEO компании Tesla, то заработать деньги можно только предложив людям то, что им действительно нужно. При этом сам инженер чаще всего «не видит» клиента и не понимает его потребностей — этим занимаются отделы технического маркетинга, продаж и тп. При этом разработчик чаще всего занимается очень специфическим направлением продукта в течение довольно долгого времени.

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

Конечно же остается конкуренция на уровне бизнеса. Есть разные стратегии коммерциализации open-source проектов. На рынке Hadoop-платформ есть три основных основных коммерческих игрока: Hortonworks (группа разработчиков Hadoop-системы в Yahoo!), Cloudera и MapR (очень мал). Все остальные, кто пытался строить свои дистрибутивы (IBM, Intel), в итоге либо ушли на какую-то из первых двух, либо пользуются Apache Bigtop для построения своих собственных платформ из канонического кода Апачи-проектов (Google DataProc и Amazon EMR).

Но они по-разному продают эти платформы своим клиентам. Cloudera добавляет поверх open-source  какие-то коммерческие закрытые компоненты вроде системы для менеджмента кластеров. MapR, например, добавил свою файловую систему, до боли напоминающую NFS. Hortonworks, с другой стороны, абсолютно все отдает в открытую — все их новые разработки идут в Apache. Этим они привлекают клиентов, демонстрируя, что у них все открыто и клиент может сам взять код и продолжать делать свое решение независимо.

Есть такое хорошее выражение: «There is no boss in open-source». Единственное требование — чтобы сообщество принимало твои наработки. Есть определенные стандарты, которым нужно соответствовать: качество кода, следование определенным технологическим принципам. Как только разработчик начинает соответствовать этим требования, он можешь участвовать в любом кусочке проекта и никто его не остановит. При этом, никто не заставляет заниматься этим конкретным фрагментом всю оставшуюся жизнь. Это дает возможность профессионально расти, улучшать свои навыки и наращивать технологическую базу.

А вы как open-source fellow в EPAM можете сказать, сколько процентов разработчиков компании коммитят в какие-то open-source проекты?


Процент оценить довольно тяжело, у нас все-таки больше 20 тысяч разработчиков. Но за последние шесть месяцев мы запустили интересный проект, в рамках которого целенаправленно контрибьютим в Apache Ignite, Apache Flink, Apache Zeppelin. Для понимания — в последней версии Apache Flink, из 100 contributors 10 человек сотрудничает с EPAM. У компании есть порядка 20-30 продуктовых проектов в open-source — начиная от управления тестовыми отчетами, веб-проектов, до анализа генома человека и обработки Big Data в облачных технологиях. Мы наращиваем экспертизу в стратегически интересных направлениях обработки Big Data для разработки продуктов и платформ для наших клиентов. На проекты можно посмотреть и поучаствовать через github.com/epam.

Если вернуться к big data и Hadoop, вы говорили, что смотрите на то, какие технологии интересны клиентам. Что это значит и как дальше будет развиваться Hadoop? Все-таки это уже достаточно зрелая технология.


Я может скажу не совсем то, что ожидают от человека, который много лет занимался разработкой компонент Hadoop-экосистемы — Hadoop  стабилизировался. Технология стала «взрослой», отработанной. Ее начали признавать в больших компаниях, стали использовать для внутренних инфраструктурных решений. Но активный цикл разработки ПО не бесконечен. Какое-то время происходит всплеск разработки, улучшений, а потом все стабилизируется. Вот эта фаза, когда наступает стабильность, называется commodity phase: технология становится обычной и доступной для всех. Hadoop перешел именно в эту фазу.

Кстати, забавно, что даже спустя 11 лет после появления Hadoop остается для многих мантрой: «надо его поставить и тогда все можно будет решить». На самом деле, нужен трезвый расчет — многие задачи решаются без распределенных вычислений или массивных кластерных платформ хранения данных. Например, весь веб-сайт StackOverflow живет на 4 серверах с SSD-дисками: мастер-копия самого StackOverflow, мастер-копия всего остального и две реплики. И они обслуживают 200 миллионов запросов в день. Сколько бизнесов в мире сталкивается с подобными объемами?

Как только технология становится commodity, в ней перестают появляться качественные улучшения. Hadoop изначально состоял из файловой системы для хранения данных и вычислительной системы для обработки этих данных — MapReduce. Он мало используется сегодня в силу своей неэффективности. Вместо MapReduce появился TEZ, Spark. Файловая система выжила, но ничего революционно нового там так не появляется. Файловая система HDFS делалась для создания больших хранилищ данных в дата-центрах. Компании вроде Amazon, Facebook, Yahoo!, Google, мобильные операторы хранят очень много данных. Но не все используют HDFS. Например, Google Spanner хорошо зарекомендовавшая себя глобально-распределенная файловая система. И все больше и больше не-инфраструктурных компаний начали уходить из своих дата-центров в облака Amazon, Microsoft или Google. А те кто остается в своих дата-центрах зачастую используют Open Stack с файловой системой Ceph.

Базовый слой, который был изначально Apache Hadoop, потихоньку начинает растворяться. Новые компоненты, которые изначально работали поверх него, начинают переключаться на работу в облачных технологиях. Это все по-прежнему называется Hadoop, хотя там уже больше 30 компонентов, из которых только два — это оригинальный Apache Hadoop. Hadoop перестал быть центром Вселенной. Не стал им и Apache Spark — он просто повторил историю Hadoop на немного другом уровне.

Сейчас идет активная миграция от собственно Hadoop в облачную область — и сокращение сегмента, где Hadoop используется для хбранения данных. Если очень нужно, можно взять кластер Spark задеплоить на AWS и не волноваться, есть ли там HDFS. Большинство бизнесов и разработчиков фокусируются на обработке данных, бизнес-игроков мало интересует как они хранятся. Скорость обмена данными с клауд файловой системой ниже чем с HDFS. И даже сильно ниже, если Вы не боитесь использовать решения Microsoft и пошли в Azure. Зато не надо тратить деньги на собственных специалистов, системных администраторов, покупать железо, которое устаревает быстрее, чем ломается. Побеждает простое и эффективное решение.

Например, Amazon много работал над реализацией self-serving system. Такая система не требует специалистов для запуска и обслуживания — любой человек может зайти в ее консоль, «нажать кнопочку», поднять кластер для обработки данных, закачать их и обработать. Набор навыков тоже становится commodity. Компании не нужен свой системный администратор для обслуживания сотни компьютеров, если есть 5 сисадминов в Amazon, которые обслуживают 100 тысяч серверов. Для запуска ресурсов в облаке и, может быть, релизов ПО нужен Devops.

Вычислительная мощности тоже становятся данностью — и люди фокусируются на том, какую пользу можно извлечь из данных: лучше продавать товары в магазинах или оптимизировать маркетинговое предложение, предсказав поведение клиентов. Технологическая часть обработки данных активно двигается в направлении predictive analytics — моделирования поведения, к примеру, покупателей. Мы обрабатываем исторические данные и строим модель, которая подскажет нам, что может случится. Второе актуальное направление — prescriptive analytics, когда мы обрабатываем объемы данных и делаем вывод, что для того, чтобы сделать на 10% больше продаж, нам нужно определенным образом поменять нашу маркетинговую стратегию.

Невозможно на 100% точно предсказать или смоделировать будущее. Но при условии, что подобные подходы позволяют поднять доверительный интервал результата с 25% до 60% — это, пусть и небольшая, но победа.

Одна из интересных реализаций такого подхода применяется в промышленности, где задействуюется много сложных агрегатов — а именно, построение аналитики даты и типа поломки, которая с ними случится. Один из примеров – добыча нефти и газа. Представьте себе океанскую платформу для добычи нефти — она состоит из миллиона различных компонентов. Если в насосе ломается произвольный подшипник — вся платформа останавливается на неделю. Деталь закажут, изготовят и доставят вертолетом с континента. Вместо этого, благодаря анализу информации с сенсоров, мы можем заранее заметить аномальные признаки, например повышение температуры насоса или вибрацию.

Используя методы машинного обучения и анализируя исторические данные с оборудования, мы теоретически можем предсказать с доверительным интервалом в, например, 94%, что этот подшипник выйдет из строя в следующие 4 дня. Заказав и получив этот подшипник заранее, производственный процесс можно планово приостановить на 30 минут, в итоге сэкономив миллионы.

Все эти технологии становятся все более доступными для не-программистов и все более ориентированными на типичного пользователя. В связи с тем, что данных становится все больше, должна расти и скорость из обработки — иначе теряется смысл всей игры. Поэтому все чаще можно встретить in-memory computing платформы, которые полностью работают в компьютерной памяти. Среди них Apache Ignite, изначально разработанный в компании GridGain, и Apache Geode, пришедший из Pivotal.

Распределенная обработка данных в памяти получает все больше внимания больших технологических компаний. Удивительный факт: компьютерная память подешевела за последние 20 лет почти в 100 000 раз. В номинальных долларах, без учета инфляции в почти 60% за то же время. Как мне кажется, это одно из направлений, где будет происходить много увлекательного и интересного в следующие годы — о других направлениях и трендах мы поговорим на JavaDay 2017.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332856/


Метки:  

No rest for the wicked. Фотоотчет из дальних уголков России, где мы оказались благодаря Росгидромету

Среда, 12 Июля 2017 г. 13:00 + в цитатник
Но что странное, что непонятнее всего, это то, как авторы могут брать подобные
сюжеты, признаюсь, это уж совсем непостижимо, это точно…
нет, нет, совсем не понимаю.
Н. В. Гоголь


Волею судеб я стал участником грандиозного проекта ЛАНИТ – модернизации метеорологической сети Росгидромета. Практически нигде в цивилизованном мире наблюдатели не носятся по площадке, чтобы снять приборные показания – все, что можно, автоматизировано. В России с этим подзадержались, но благодаря проекту модернизации Росгидромета случилось и переоснащение метеосети. Таких масштабов не было нигде и никогда, но мы реализовали проект всего за два года (2008-2009 гг.). А это, на минуточку, поставка 1842 метеостанций плюс прочего связного и энергетического обвеса. Еще нужно было собрать станции, скомплектовать и упаковать, доставить их в каждый из 85 областных центров, а оттуда уже развозить на станции, устанавливать и настраивать.

В настоящий момент вовсю идет второй этап модернизации. Раскопки в архивах документов и натолкнули меня на мысль о таком посте.



Трагедия в масштабе 1:4 000 0000. География метеостанций Росгидромета

В целом, весь проект модернизации организаций и учреждений Росгидромета состоял из нескольких контрактов: по метеорологии, гидрологии, аэрологии, океанологии и т. д. Дальше я покажу фотографии, которые касаются самых примечательных из них.

Та часть проекта, которую мы охватили, включала поставку оборудования для более 2000 объектов наблюдательной сети и проведение инсталляций более, чем на 500 площадках.

1. Метеосеть


Оборудование


Чтобы реализовать проект, ЛАНИТ стал производителем метеостанций. Мы решили, что развернем это производство своими силами на базе завода «Луч» в Новосибирске. Компоненты везли со всего мира, мы получали и российские составляющие (с ними, по традиции, было больше всего проблем).

Новосибирск, завод «Луч». Производство нашего оборудования

На заводе организовали целую линию сборки, на которой трудилось 10-15 человек. Ради этого мы несколько раз привозили толпы спецов по организации производства из компании Vaisala, кои делились своими знаниями, не ведая страха и упрека.

Потом станции проходили через упаковочный цех. Также «Луч» делал металлические изделия – мачты, боксы, стойки, траверсы и т. д. Они же собирали станции, тестировали и упаковывали.

Установка компонент на монтажную раму станции

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

Источник
Типичное поведение складского персонала


Чтобы уложиться в бюджет, в проект вписали шеф-монтаж. Мы объехали 23 территориальных управления (УГМС) Росгидромета. Собирали там местных специалистов управлений, учили технарей, как устанавливать и обслуживать станции, а методистам рассказывали, как работать с новым оборудованием и ПО. Практику закрепляли как раз шеф-монтажами. Дальше уже эти подготовленные инженеры управлений самостоятельно устанавливали комплексы и обучали на метеостанциях наблюдателей.  

У нас под шеф-монтаж было задействовано до 12 бригад, в каждой по 2 человека.

Курск, обучение. Здесь должна была быть шутка про отопление, но я ее не смог придумать

Забыть про бабушкины методы


Раньше наблюдатель, который (а обычно — которая) получает несколько тысяч рублей в месяц, должен был 8 раз в сутки в любую погоду пойти на площадку, залезть на лесенку, достать термометры, записать показания и т.д. Сейчас на большинстве объектов Росгидромета современные автоматизированные станции сменили ртутные барометры, гигрографы и прочие устаревшие инструменты метеоролога.

Ручные наблюдения в итоге никуда не делись (классический пример – определение формы облаков), но на отдельных пунктах, не относящихся к основной наблюдательной сети, станции переводят на полностью автоматический режим.

А это побочный продукт – мобильная (разборная) метеостанция

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

Студенты учились в каком-то метеорологическом вузе и задумали поехать на юг. Раньше все было проще — позвонили на одну из южных метеостанций:

— Мы – студенты, скоро приедем. Будем у вас тут жить.
— Да, пожалуйста, приезжайте.
Приезжают – никого нет, только один маленький мальчик лет 10-11 ходит.
Студенты спрашивают:
— Мальчик, а где все?
— А они в соседнюю деревню поехали на свадьбу.
Проходит пара дней, а родителей все нет. Идут к мальчику:
— Мальчик, а родители-то где?
— Так они на две недели уехали.
— Хорошо, но это же метеостанция, здесь каждый день нужно дежурить, в сроки все записывать и передавать.
— А, ничего. Они на две недели вперед все написали.

Вот он, наш герой. Логгер

В нашей метеостанции самое уникальное – это программная составляющая. Я о скриптах, или конфигурации. Логгер QML201 весьма наворочен. Вот мы и делали всякие невообразимые штуки, которые вряд ли кто-то с тех пор повторял. Пример: Есть ключевой код для передачи метеорологической информации. Речь о КН-01, который придуман в лохматых годах и был заточен исключительно под телеграф. Основная обработка данных лежала на наблюдателе, а в нашем случае надо было изрядно подгрузить логгер, вместо того, чтобы посылать первичные данные в центр и обрабатывать уже там.  

Несмотря на наши бурные протесты, нам пришлось реализовывать это чудо в логгере. Да еще и с досылкой данных от наблюдателя. Не прошло и 8 лет, как нам удалось что-то изменить.

Помимо метеостанций, в продолжение проекта попало 18 актинометрических станций, которые измеряют все виды солнечной радиации.

Актинометрическая станция в Хабаровске

И морские надводные станции:

Сочи. Морской буй измеряет тонну погодных и подводных параметров.

Он же, но без массовки

ID tag

Вот эта табличка, кстати, сэкономила нам всем много денег. Через пару месяцев после установки буй сорвало с якоря штормом. Отправился он, надо думать, в Стамбул, но был перехвачен доблестными пограничниками и доставлен владельцам.

И подводные:

Санкт-Петербург, установка придонного профилографа

На маяке Толбухин

Пруф

Into the great wide open


Карачаево-Черкесия


Конечно, до всех станций руководитель проекта не может доехать – на это просто нет времени. Но однажды я махнул на все рукой и сам поехал в Карачаево-Черкесию, на Клухорский перевал. Это рядом с Домбаем. У этой местности статус труднодоступной территории. По определению, грубо, «труднодоступная станция» – это куда на машине не доехать, на коне не доскакать. А на Клухорский перевал вполне можно доехать и пожить жизнью местных. Единственное, чего там нет, так это связи.

Источник

Поселок Клухор


Источник


 
Клухорский перевал — самый высокогорный участок Военно-Сухумской дороги (высота 2781 м), ведущей от великих Кавказских гор к Черноморскому побережью. Здесь проходит граница России с Абхазией. Именно в этом месте во время Второй мировой войны проходили жесточайшие бои с немецкими оккупантами за перевал Клухор.

Клухорский перевал и датчик ветра. Созданы друг для друга

На Клухорском перевале мы работали в августе, стояла прекрасная погода. Точнее, тут видно, кто работал, а кто нет

Те самые ручные (табельные) средства измерения

Настройка КВ-радиостанции

После Клухора решил остаться на монтаж автоматической станции в Зеленчукской обсерватории.


А точнее – в специальной астрофизической обсерватории научно-исследовательского института Российской академии наук на Северном Кавказе.


В настоящее время это крупнейший российский астрономический центр наземных наблюдений за Вселенной.  На фотке — оптический рефлектор БТА и я. Постарайтесь не перепутать.

Зеленчукская обсерватория. Метеостанцию поставили прямо у гостиницы. Зачем далеко ходить?

А дальше, как в анекдоте про Буратино и сломанную ногу, пошло-поехало…

2. Модернизация аэрологической сети


Этот контракт включал поставку и установку 60 аэрологических локаторов по всей стране. Ниже – про одну из локаций.

Якутия, остров Котельный


Наш проект коснулся таких мест, куда действительно ни на чем не доберешься, кроме вертолета.
Так, команда ЛАНИТ отправилась на остров Котельный в Якутии. Он расположен между Восточно-Сибирским морем и морем Лаптевых и является наиболее крупным в архипелаге Новосибирских островов.   

Источник

Добраться из Москвы до Котельного предельно просто. Почти 7 часов лететь до Якутска на рейсовом самолёте. Затем необходимо долететь до Тикси – это еще три часа, а оттуда до Котельного «рукой подать» – еще всего лишь три часа на вертолете над морем с дозаправкой на острове Столбовой или судном денек-другой.

Пару раз в год экспедиция забрасывает на станцию консервы и топливо. Под это дело закинули и локатор с материалами.


Доставить оборудование на корабле можно только в короткий сезон навигации.

Это лодка, море и солнце

Специальные мужики сгружают радиопрозрачное укрытие

Они же сгружают остальные части локатора


Климат на острове арктический, суровый. Снег лежит 9-10 месяцев в году. Средняя температура июля составляет +2,9 С. Температура ниже -30 градусов С может наблюдаться с октября по апрель.

Монтаж вышки для установки нового аэрологического комплекса

Белые медведи частенько наведываются в гости

Комиссия из местных коллег

Привет


Многие спрашивают, сложно ли было таким проектом руководить.
Ответ – да. Если бы на меня свалилось сразу несколько таких контрактов, я бы, наверное, скрипнул и до сих пор бегал в каске и улыбался.

Но в целом, я достаточно плавно погружался в эту историю и, по аналогии с детскими сочинениями, погружал туда свою команду. Да и мне было очень интересно этим заниматься: о таком проекте можно детям-внукам рассказывать. Это в общем-то и грело, по большей части.

3. Метео-2


Как я уже писал, спустя почти 10 лет после начала первого проекта стартовал второй проект модернизации Росгидромета. Где, в том числе, мы заполучили контракт про продолжение модернизации метеосети. Ниже совсем свежая фотка недельной давности – установка станции нового поколения.

Новая метеостанция в Центральном УГМС.  Самолеты теперь не страшны.

Сферическая поверочная лаборатория в вакууме


Ну и напоследок – за время работы с Росгидрометом я волей-неволей стал там одним из своих. Когда поздравляешь людей с профессиональным праздником, нередко можно услышать в ответ: «Взаимно, и Вас также». Вот это по-настоящему круто =)

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332888/


Метки:  

[Из песочницы] Разворачиваем и демонизируем ASP.NET Core приложение под Linux в виде фонового сервиса

Среда, 12 Июля 2017 г. 12:49 + в цитатник
Доброго дня, господа. Сегодня мы затронем тему, за обсуждение которой ещё два года назад сожгли бы на костре — запуск ASP.NET приложения под linux. В данной статье будет использоваться Ubuntu 16.04

Подготовка окружения


Для начала, добавим dotnet-репозиторий:

sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893

На выходе получаем примерно следующее:

image

Теперь обновим индекс наших пакетов:

sudo apt-get update

Далее, мы можем просто установить dotnet-пакет при помощи apt-get:

sudo apt-get install dotnet-dev-1.0.4

Теперь можем смело приступать к созданию приложения

Создание приложения


При помощи команды dotnet new мы можем создать шаблон для нашего приложения, подобно шаблонам из Visual Studio. Подробная документация к команде.

На текущий момент (07.2017), команда dotnet new поддерживает следующие шаблоны:

image

Мы создадим веб-приложение ASP.NET Core:

dotnet new mvc

На выходе консоль выдаст нам следующее сообщение:

image

Чтобы убедиться, что шаблон сгенерировался правильно, заглянем в содержимое папки при помощи команды ls -la.

image

Все необходимые папки для сборки приложения на месте, приступим! Для начала, восстановим все пакеты при помощи dotnet restore.

image

Теперь можем собрать приложение:

dotnet build

image

Запустим приложение с помощью:

dotnet run

image

Консоль говорит нам, что приложение запустилось по адресу localhost:5000/. Проверим:

image

Желающих подробнее узнать, как работает web-сервер отсылаю к официальному источнику.

Теперь убьём процесс нажав Ctrl + C и опубликуем приложение командой dotnet publish. Эта команда упаковывает приложение и все его зависимости для дальнейшего развёртывания (желающим интимных подробностей сюда).

В случае проблем с правами доступа Вам поможет команда sudo chmod и эта страница документации.

Развертывание на сервере.

Если мы хотим развернуть наше приложение под linux-сервером, необходимо настроить прокси и демонизировать процесс запуска приложения. Для проксирования мы будем использовать nginx, для демонизации процесса systemd. Краткое описание утилиты

Создаём прокси-сервер.

Как следует из документации выше, с asp.net core в коробке идет kestrel — веб-сервер для asp.net приложений. Зачем нам тогда нужен прокси-сервер? Ответ даётся на официальной странице Microsoft:

image
Если вы выставляете ваше приложение в интернет, Вы должны использовать IIS, Nginx или Apache как обратный прокси-сервер.

Если Вы не знаете, что такое обратный прокси-сервер

Обратный прокси-сервер получает HTTP запросы из сети и направляет их в Kestrel после первоначальной обработки, как показано на след диаграмме:

image

Главная причина, по которой следует использовать обратный прокси сервер — безопасность. Kestrel относительно нов и ещё не имеет полного комплекта защиты от атак.

Ещё одна причина, по которой следует использовать обратный прокси-сервер — наличие на сервере нескольких приложений, использующих один порт. Kestrel не поддерживает разделение одного порта между несколькими приложениями.

Так же, использование обратного прокси-сервера может облегчить распределение нагрузки и поддержку SSL.

Как говорилось выше, в качестве прокси-сервера мы будем использовать nginx.

Т.к. в качестве прокси-сервера у нас используется не IIS, следует добавить следующие строки в метод Configure файла Startap.cs.

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

Здесь мы включаем поддержку ForwardedHeaders мидлвера из пакета. Microsoft.AspNetCore.HttpOverrides, который будет вставлять в Http-запрос заголовки X-Forwarded-For и X-Forwarded-Proto, использующиеся для определения исходного IP адреса клиента и передачи его прокси-серверу. Определение мидлверов и практическое использование так же будет рассмотрено в дальнейших частях этого гайда.

Если у Вас nginx не установлен, выполните следующую команду.

sudo apt-get install nginx

и запустите его командой:

sudo service nginx start

Далее, нам необходимо сконфигурировать nginx для проксирования http-запросов.

Создадим файл /etc/nginx/sites-available/aspnetcore.conf. Папка sites-avalible укахывает nginx-у, какие веб-сайты доступны на текущем сервере для обработки. Добавим в него следующие строки:

server {
    listen 8888; # указываем порт, по которому nginx будет слушать запросы
    location / {
        proxy_pass http://localhost:5000; # указываем порт нашего приложения
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Создадим символическую ссылку на aspnetcore.conf в папку sites-enabled, в которой отражаются запущенные nginx-ом сайты.

sudo ln -s /etc/nginx/sites-available/aspnetcore.conf /etc/nginx/sites-enabled/aspnetcore.conf

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

Nginx настроен на то, чтобы принимать запросы с localhost:8888. Перезапускаем nginx командой sudo service nginx restart, чтобы созданные нами конфигурационные файлы вступили в силу. Проверяем:

image

502-я ошибка говорит, что сервер перенаправляет нас в другое место и в этом месте что-то пошло не так. В нашем случае — я убил процесс с нашим веб-приложением, которое было ранее запущено командой dotnet run. Потому что могу :)

На самом деле, потому что запускать dotnet run в консоли и вечно держать эту вкладку открытой грустно. Именно поэтому процесс будем демонизировать, то есть настроем автозапуск после перезагрузки и автоматическую работу в фоне с помощью systemd.

Для этого создадим файл в директории /etc/systemd/system/ с расширением .service

Назовём его kestrel-test:

sudo nano /etc/systemd/system/kestrel-test.service

И положим в него следующее содержимое:
[Unit]
Description=Example .NET Web API Application running on Ubuntu

[Service]
WorkingDirectory=/home/robounicorn/projects/asp.net/core/test-lesson/bin/Debug/netcoreapp1.1/publish #путь к publish папке вашего приложения
ExecStart=/usr/bin/dotnet /home/robounicorn/projects/asp.net/core/test-lesson/bin/Debug/netcoreapp1.1/publish/test-lesson.dll # путь к опубликованной dll
Restart=always
RestartSec=10 # Перезапускать сервис через 10 секунд при краше приложения
SyslogIdentifier=dotnet-example
User=root # пользователь, под которым следует запускать ваш сервис
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

Теперь включим и запустим сервис при помощи следующих команд:

sudo systemctl enable kestrel-test.service
sudo systemctl start kestrel-test.service

Проверим статус сервиса:

sudo systemctl status kestrel-test.service

Если всё было сделано правильно, на эта команда выдаст нам следующее:

image

Перейдём по ссылке ещё раз:

image

Ну вот, дело в шляпе. Спасибо за внимание!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332920/


Метки:  

Как за 15 лет вырастить лучшую службу поддержки

Среда, 12 Июля 2017 г. 12:05 + в цитатник
Человек выбирает хостинг по цене и отзывам. Решает, остаться ли в компании — по доступности сервера и качеству поддержки. Большинство наших клиентов не занимается администрированием сервера, и для них очень высока ценность качественной поддержки.

15 лет мы колдовали над саппортом: менялись в деталях и устраивали революции. Тестировали новое, отказывались от неуместного и праздновали находки. Добились отличного соотношения между скоростью и качеством ответов, но не остановились на этом.

Расскажем, как выросла служба поддержки FirstVDS.


2002 — 2007 год: Ну очень крутая поддержка


среднее количество запросов в месяц — 540
среднее время ответа — 26 мин

Данные с 2003 по 2007 год, цифры за 2002 год не сохранились

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

К 2005 начала формироваться техническая поддержка — наняли 4 человек. Загрузка была небольшая, и они справлялись отлично — отвечали быстро и полно.

Консультирование по техническим вопросам было бесплатным, настройка и установка — платной. Техспециалисты работали посменно и выполняли определённые задачи за фиксированную стоимость. Перенос сайта — столько-то, оптимизация скриптов — столько-то и т.д.

Общие вопросы (активация услуг, аккаунтов, обработка заказов) решал другой отдел — по работе с клиентами. Он состоял из 2 человек. Сотрудники отдела также отвечали на вопросы с финансами и документами. Решали все свои задачи бесплатно.

Письма и звонки получал отдел по работе с клиентами и, если вопрос технический, передавал поддержке. Запросы, которые поступали через личный кабинет (ЛК), видели оба отдела — выбирали свои и обрабатывали в тикетнице.



В 2008 году клиентов стало значительно больше. 6 специалистов уже не справлялись.


2008 — 2010 год: Оставаться крутыми всё сложней


среднее количество запросов в месяц — 2302 (+1762)
среднее время ответа — 8 мин (-18 мин)


Увеличилось число запросов, а с ним и время ответа. Чтобы отвечать так же быстро и качественно, мы наняли ещё людей: троих в отдел по работе с клиентами и четверых в техподдержку. Отделы работали по старой схеме.



Среднее время ответа уменьшилось в 3 раза! Но клиенты всё больше жаловались, что технические работы платные.

Да… ещё немного, и ваш сервис станет таким же отстойным, как и все остальные. Деньги дерут за всё. Рекомендую на пополнение счёта комиссию установить, на запуск passwd и ещё на каждый логин в ISPmanager


Мы периодически снижали цены и, наконец, решили, что бесплатная техподдержка — отличное конкурентное преимущество.


2011 — 2012 год: Бесконечная очередь из тикетов


среднее количество запросов в месяц — 3322 (+1020)
среднее время ответа — 27 мин (+19)


Чтобы обойти конкурентов, мы сделали техподдержку бесплатной. Платными остались только сложные технические работы — установка и настройка ПО, перенос сайтов и баз данных.

Мы предвидели, что тикетов станет много, и скорость ответа уменьшится. Клиенты будут недовольны, потому что привыкли получать отклик быстрее. Тогда мы ввели очередь — когда клиент видит номер в очереди, он оценивает примерное время ответа.

Очередь формировалась по весу. В начало очереди попадали клиенты, которые с нами дольше, больше платят и меньше спрашивают, в конец — новенькие и проблемные (ну, бывают такие).

Можно было подняться в очереди за деньги: в начало — 200 руб., за первыми в очереди — 50 руб. Клиенты со срочными проблемами могли решить вопрос в ускоренном порядке.



Проблема в том, что клиенты в конце очереди почти не продвигались выше. Их постоянно обгоняли те, кто доплачивал. Или приходили ещё «старые и дорогие», и конец очереди отодвигался дальше и дальше. Чтобы ответить всем, мы ускорялись и теряли в качестве. В результате всё равно отвечали недостаточно быстро и полно.

Проблему с огромной очередью надо было решать.


2013 — суровые сортировщики


количество запросов в месяц — 3853 (+531)
среднее время ответа — 27 (+0)


Чтобы отвечать быстро, качественно и не обижать клиентов в конце очереди, мы сделали несколько вещей.

Первое — вернули платную техническую поддержку, но в новом виде. Добавили два пакета, они действовали месяц и включали решение нескольких задач по администрированию сервера. «Базовый» стоил 250 руб., включал решение 5 задач и предполагал ответ в течение 24 часов, «Премиум» — 10 задач за 2000 руб. и ответ за 5 часов. От последнего вскоре отказались, он был не востребован.

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

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

Четвёртое и главное — добавили к техподдержке первую линию — сортировщиков. Они общались с клиентами по телефону. Отвечали в тикетах на общие и простые технические вопросы, например, на какие сервера имён ведёт домен. Предпродажные, финансовые и сложные технические вопросы передавали соответствующим отделам.



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

Мы думали, что после сортировки техподдержка, финансы и продажи будут получать только целевые запросы, и скорость ответов увеличится. Но ожидания не оправдались. Получилась та же очередь, но в других отделах — клиенты снова негодовали.

Вторая причина недовольства клиентов — проблемы в общении. Все запросы сначала падали первой линии. Сейчас понятно, что там клиентов должны были «встречать» вежливые и внимательные консультанты. Но лицом компании были сортировщики, которые отвечали по шаблону или перекидывали в другой отдел. Если не могли помочь, не знали как объяснить причину. Общались сухо и официально, отвечали только по делу. Их задачей было передать тикеты, общаться с клиентами сортировщиков не учили.

Мы расстроились, но от деления на несколько линий не отказались. Идея была хорошей, осталось сделать первую линию более приветливой и функциональной.


2014 год — служба заботы о клиентах


среднее количество запросов в месяц — 4105 (+252)
среднее время ответа — 19 мин (-8)


Сортировщики не справлялись со своей задачей, и мы сделали из первой линии отдел заботы о клиентах. Вся суть изменений в названии — мы обучили сортировщиков общаться с клиентами. И набрали 5 новых сотрудников.

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

Компетенция первой линии расширилась. В отдел заботы о клиентах перешли общие вопросы, добавились простые технические, например, создание домена на сервере. Сотрудники отвечали в тикетах и по телефону.

В этом же году мы добавили на сайт чат.



Техподдержка обновила свой регламент, и ряд техвопросов средней сложности повис в воздухе. Например, установка расширений PHP, ПО на сервере, определение вредоносных скриптов, рассылающих спам. Первая линия не знала, как на них отвечать, у второй не было времени.


2015 год — техконсультанты меж двух огней


количество запросов в месяц — 5040 (+827)
среднее время ответа — 33 мин (+14 мин)


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

Админы считали, что с такой ерундой могла бы справиться и первая линия. Первая линия не считала эти вопросы ерундой. Тогда мы добавили техконсультантов. Эти ребята стали буфером между первой линией и админами. Они взяли на себя технические вопросы средней сложности — задачи, которые можно решить за 15 минут. В обязанности консультантов вошла помощь при падении службы сервера (Apache, Nginx), неверно настроенных правах на файлы сайта, ошибках с указанием параметров подключения баз данных и др.

Сложные технические вопросы (замена IP-адресов, перенос сайтов/баз данных, настройка резервного копирования и др.) консультанты передают админам.

С добавлением ещё одной линии поддержки понадобилось дополнительное время на передачу тикетов. Скорость ответов снизилась, зато увеличилось их качество. Главное, появление техконсультантов разгрузило админов, конфликт с первой линией был снят.



Общие, предпродажные, финансовые, простые и средние по сложности техвопросы мы решали бесплатно. Закрывали около 80% тикетов. Но клиенты всё равно негодовали, что наша поддержка платная.


2016 год — платная или бесплатная поддержка?


среднее количество запросов в месяц — 7002 (+1962)
среднее время ответа — 17 мин (-16 мин)


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

К 2016 году бесплатной была помощь первой линии и техконсультантов, но даже это не удовлетворяло клиентов.

Мы изучили конкурентов и поняли, что дело в названии. Переименовали сложную техническую поддержку в администрирование — когнитивный диссонанс исчез, клиенты перестали ругаться.

В этом же году полноценно заработал отдел качества. В 2015 мы анализировали причины ухода клиентов, исправляли то, что зависело от нас. К концу года ввели систему оценки чатов и тикетов — лайки/дизлайки от клиентов. Задачей отдела качества стало проверять правильность и полноту ответов, справедливость оценки клиентом. По результату сотрудников поддержки начали штрафовать или поощрять материально. Поддержка стала отвечать ещё качественнее, а благодаря мотивации и 5 новым сотрудникам ещё и в 2 раза быстрее.


2017 год — секс по телефону теперь бесплатно


среднее количество запросов в месяц 6458 (-544)
среднее время ответа мин 14 (-3)


Похожая история получилась с платной линией техподдержки. Клиенты звонили на 8-800, слышали «Если вы хотите обсудить технический вопрос… оставайтесь на линии. Стоимость звонка 45 руб./мин. без НДС». Клиенты бросали трубку и шли писать негативные отзывы.

Коллеги из VDS, а у вас все в порядке? 45 руб за минуту поговорить с техподдержкой? Секс по телефону и то дешевле. Брррр


Они считали всю техподдержку по телефону платной, потому что недослушивали голосового меню —
«… или дождитесь, пока ваш звонок будет переведён в Службу заботы о клиентах».

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



Это последнее изменение в поддержке FirstVDS на сегодня, но не последнее в истории проекта. Уже 15 лет мы меняемся, чтобы стать понятнее, ближе и удобнее. Главный критерий изменений — ваш отклик.
С каждым годом вас всё больше — всё сложнее учитывать мнение каждого, отвечать быстро и качественно, но мы стараемся.



За последние 2 года поддержка FirstVDS увеличилась в 2 раза. Сейчас в 3 линиях 37 человек. Мы посещаем курсы, прослушиваем звонки, измеряем время ответа. Дополняем регламент, используем систему мотивации сотрудников.

За 15 лет мы многое изменили, но ещё есть, куда стремиться. Поддержку FirstVDS ругают и хвалят. Мы используем конструктивную критику, чтобы стать лучше, и радуемся тёплым словам. Ставьте нам лайки/дизлайки в тикетах, пишите отзывы на сайте — нам нужен ваш отклик.

Хотите подробностей, как устроена поддержка FirstVDS сейчас, почему мы считаем текучку положительной, и сколько руководителей вышло из саппорта? Подумываем об ещё одной статье про поддержку. Пишите в комментариях, что вам интересно.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332820/


Метки:  

[Из песочницы] Реализуем тач логгер под Android с помощью CVE-2016–5195

Среда, 12 Июля 2017 г. 11:32 + в цитатник

История о том, как уязвимость в ядре linux помогает мне собирать данные для диссертации


Пару лет назад я решил выяснить, можно ли идентифицировать человека по жестам, которые он вводит на экране смартфона. Некий «клавиатурный почерк», но только для сенсорного экрана. Чтобы это понять, нужно проанализировать сотни тысяч жестов от множества разных пользователей. Но… Как собрать эти данные на смартфоне?

Я расскажу о своём пути решения этой задачи. Он был долгим, тернистым, но чертовски увлекательным! Надеюсь, вам будет интересно проследить за ним и узнать для себя что-то новое о linux, android, их безопасности и их внутренностях. Я не гуру в устройстве linux, поэтому кому-то некоторые объяснения покажутся очевидными и излишне подробными, но повторюсь, это мой путь и я подробно описываю всё, что изучил в процессе. Надеюсь, это не оттолкнёт опытных линуксоидов и немного снизит порог вхождения для всех остальных. Итак. Как же реализовать тач логгер под android?

Выбор пути


Самое простое и очевидное решение — написать отдельное приложение и собирать параметры жестов только в нём, как это сделано в [1]. Но это совсем неинтересная задача. И не только потому, что это слишком просто. Ограничив сбор данных одним приложением, есть риск упустить какие-то важные поведенческие характеристики пользовательских жестов, которые проявляются только при особых условиях. Поэтому хотелось бы собирать жесты вне зависимости от приложения, в котором работает пользователь, правда сделать это не так просто.

Мобильные ОС защищены на порядки лучше настольных. Они, в отличие от последних, разрабатывались уже в те времена, когда над безопасностью и изоляцией процессов серьёзно задумывались, и тот же Android просто не позволяет вам отслеживать пользовательские жесты за пределами view вашего приложения. Подозреваю, в других мобильных ОС такая же ситуация. Но неужели всё настолько хорошо защищено, что для получения координат касаний пришлось прибегнуть к помощи уязвимостей ядра linux?

Многие сейчас, например, вспомнили про android accessibility service — сервис универсального доступа. Удобная штука для помощи людям с ограниченными возможностями или написания различного рода троянцев под Android. Можно подписаться в приложении на события универсального доступа и получать в том числе данные о пользовательских жестах. Казалось бы! Бери и пользуйся, всё уже написано за тебя. Но увы, нужные мне параметры жестов из этих данных не извлечь.

Что ж, остаётся не так много вариантов. Попробуйте, например, включить в меню разработчика пункт «отображать касания».



После этого следы ваших жестов будут оставаться на экране, в каком бы приложении вы ни находились. А значит, этот код как минимум имеет доступ к информации о координатах касаний. Интересно, где он взял эти данные и есть ли там ещё? В поисках ответа погружаемся в исходники android и обнаруживаем сие:


Выделенная строчка позволяет объекту mPointerLocationView обрабатывать все события ввода, поступающие от сенсорного экрана

Если изучить иерархию классов и граф вызовов, станет ясно, что в итоге все сводится к вызову метода registerPointerEventListener у системного сервиса WindowManagerService. Последний через интерфейс WindowManager позволяет обычным приложениям вызывать свои методы удалённо. Кажется, достаточно получить в приложении доступ к WindowManager, вызвать этот метод, и приложение сможет обрабатывать координаты, приходящие из системы. Но есть подвох: в Android доступ к системным сервисам предоставляется через Binder IPC, и у WindowManager можно вызвать лишь те методы, которые находятся в соответствующем AIDL файле. К сожалению, нашего метода в AIDL нет, и удалённо вызвать его из пользовательского приложения не удастся. Но если вы вендор, и не знаете, как ещё можно следить за пользователями, можно изменить исходники android и добавить логгирование жестов прямо в прошивку устройства, что я и сделал ради интереса здесь. Правда в нашем случае такое решение не подходит. Можно прошить одно, два устройства для сбора данных, но десятки незнакомых тестовых пользователей на такой шаг не пойдут. Необходим более универсальный способ.

Что ж, вот оно, решение, которому я всеми силами пытался найти альтернативу, но не смог. Итак, Android основан на ядре linux, и архитектура системы ввода устроена так же, как и в любом linux дистрибутиве. Драйверы устройств ввода, тачскрина в том числе, передают данные в userspace через символьные устройства, расположенные в /dev/input/, откуда их можно прочитать. И уж напрямую от драйвера можно получить все данные, которые только можно пожелать: координаты, метки времени, площадь касания, тип жеста… Хватит не на одно исследование. Правда, здесь тоже есть подвох: в Android доступ к этим устройствам имеет либо root, либо пользователь, состоящий в группе input. Android приложения же запускаются от имени пользователей, которые такими правами похвастать не могут.

Выход из такой ситуации — запускать приложение для сбора данных на рутованных устройствах. Именно с этой целью я написал вторую версию своего тач логгера.

Эту версию я и использовал для сбора данных. Желающих помочь нашлось достаточно, но лишь у некоторых устройства были рутованы, так что собрал я не так много. Если найти способ собирать данные без установленного root, подходящих тестовых устройств станет на порядки больше, что даст нам сотни и сотни мегабайт ценных данных для анализа. В этом нам и поможет одна замечательная уязвимость в ядре linux.

CVE-2016–5195 aka DirtyCOW



Собственной персоной

История обнаружения и исправления этой уязвимости сама по себе очень занимательная. Ее случайно заметили 11 лет назад, но так и не закрыли. Повторно она была обнаружена и уже исправлена только в 2016 году, после обнаружения готового эксплойта, использовавшего её. Вот так уязвимость с 11-летней историей превратилась в 0-day. Что примечательно, она присутствует на всех версиях ядра, начиная ещё с 2.6.22! То есть на всех версиях android. В бюллетенях безопасности android эта уязвимость появилась в декабре 2016, а значит, исправлена она только на тех устройствах, которые получали обновления безопасности после этой даты. Nexus 5, например, так и остался ей подвержен, как и сотни других android устройств.

Что же делает dirtyCOW? Если вкратце, она позволяет перезаписать любой файл, доступный пользователю только на чтение. Под «любой» я имею в виду ЛЮБОЙ, даже если сама файловая система смонтирована только на чтение (в android именно так обстоят дела с разделом /system, где хранятся самые вкусные системные файлы). Подробно про механизм действия уязвимости можно прочитать, например, здесь. Нам же стоит помнить лишь две вещи. Первое: изменения в read-only файловой системе исчезнут после перезагрузки, и второе: нельзя менять размер файла, то есть записывать в него больше, чем он занимает места на диске.

Что ж, властью, данной нам уязвимостью, мы можем временно перезаписать любой системный файл, доступный на чтение. Казалось бы, это развязывает нам руки, можно творить все, что угодно! Но повторюсь, android неплохо защищён, и придется постараться, чтобы обойти эту защиту и добиться поставленных целей.

Идея реализации


Наша задача — из обычного приложения запустить процесс, способный читать данные из устройств ввода. Напомню, доступ к устройству ввода имеет только процесс, запущенный от пользователя с UID=0 (root) или состоящего в группе input (в android она имеет GID=1004).

Рассмотрим, например, файл /system/bin/ping. Его можно найти практически во всех android-устройствах, и он доступен на чтение и исполнение всем желающим. Что примечательно, у этого файла выставлен SUID-бит. SUID (Set owner User ID up on execution — выставить UID владельца при исполнении) — один из атрибутов файла в Linux. Обычно процесс наследует права пользователя, запустившего его. Наличие SUID бита позволяет запустить процесс с правами владельца исполняемого файла, а так же с его UID и GID. Соответственно, поскольку владельцем ping является root, то и выполнится этот файл от имени root. Понимаете, к чему я клоню? Если мы перезапишем ping с помощью dirtyCOW и запустим его, то наш код выполнится от имени root и сможет прочитать устройства ввода! Что ж господа, поздравляю, у нас получилось, все большие молодцы, расходимся… Нет.

SELinux


Да, наш план потрясающе сработает на всех версиях android вплоть до 4.2. Но на более свежих версиях могут возникнуть, а начиная с 5.0 обязательно возникнут некоторые трудности. Виной всему SELinux (Security-Enhanced Linux) — модуль ядра, реализующий мандатную политику безопасности вдобавок к уже существующей дискреционной. Вкратце о том, как это работает. У каждого пользователя, процесса, файла, сетевого порта, символьного устройства и так далее есть так называемый контекст безопасности — некоторая метка, дополнительный атрибут. В системе существует набор политик, который описывает разрешённые для пользователей и процессов операции на основании их контекстов. Все операции, которых нет в наборе политик, запрещены и строго пресекаются системой.

Сторонние приложения запускаются в процессах с контекстом untrusted_app. Уже из названия контекста следует, что прав у таких процессов немного. У файла /system/bin/ping контекст system_file, и да, SELinux не разрешает запускать system_file на исполнение из контекста untrusted_app. Поэтому запустить ping из приложения на android 5.0+ мы не сможем, как и другие системные файлы с SUID-битом.
На самом деле, контексты в SELinux состоят из нескольких частей, например, у ping контекст имеет полный вид u:object_r:system_file:s0, а у пользователя — u:r:untrusted_app:s0. В нашем случае это не имеет значения, поэтому я буду использовать более короткую запись.

Другие способы


Итак, в системе нет файлов с SUID-битом, доступных для запуска из сторонних приложений. Но что если перезаписать какой-то файл и сделать так, чтобы система сама его запустила от имени root? В android есть такое понятие, как сервис. Не тот, что запускается из вашего приложения и что-то делает в фоне. Сервис — это демон, стартующий при запуске системы.


Примеры сервисов, описанных в файле init.rc

Примечательно то, что android перезапускает сервисы, если они падают. Что ещё интереснее, многие сервисы запущены от root. В android 6.0.1 это vold, healthd, debuggerd, installd, zygote и многие другие. И да, файл /system/bin/app_process, он же zygote, доступен на чтение всем пользователям!


Сервис zygote, описанный в отдельном файле

Понемногу вырисовываются очертания нового плана. Если перезаписать /system/bin/app_process на наш payload и уронить сервис zygote, android перезапустит его и наш код в файле app_process выполнится от имени root.

Возникает вопрос, как уронить zygote. На самом деле, это самый ненадёжный момент в реализации тач-логгера. Сейчас я полагаюсь на то, что при перезаписи /system/bin/app_process нашим payload-ом zygote упадёт “сама”. Но нет никаких гарантий, что это будет срабатывать всегда. Давайте разберёмся, что происходит при перезаписи файла app_process и почему это может привести, а может и не привести к падению.

Виртуальная память и mmap


В Linux у каждого процесса есть своё виртуальное адресное пространство. В нём хранятся все нужные процессу данные: его стэк, куча, переменные среды и многое, многое другое. Это пространство транслируется ядром в физические адреса в оперативной памяти.


Хорошая визуализация этой концепции

Однако в этом же пространстве находится и исполняемый файл самого процесса, и его интерпретатор, и библиотеки из его зависимостей, и ещё много разных файлов. Хранить всё это в оперативной памяти было бы слишком расточительно, поэтому в linux есть довольно изящный механизм загрузки файлов в память процесса без расходования оперативной памяти вообще. Называется он memory-mapped file, или файл, отражённый в память. Ядро создаёт в адресном пространстве процесса страницу с содержимым файла, однако на самом деле эта виртуальная страница транслируется ядром не в оперативную память, а сразу на диск. То есть процесс работает с содержимым файла словно с большим куском выделенной памяти, хотя на самом деле он читает или пишет байты на диске. (кстати, если вы ещё не читали о принципе работы dirtyCOW, он как раз основан на багах при многопоточном доступе к отражённому в память файлу).

Именно в таком, «отражённом» виде хранится в памяти процесса его исполняемый файл. Частично он прогружается в память, но большая часть остаётся на диске. Поэтому изменения файла сразу же появляются и в памяти процесса. Допустим, после замены исполняемого файла процесс подгружает из памяти следующую инструкцию. С большой вероятностью она может прийтись «некстати» и привести процесс к падению, либо вообще окажется мусором, с тем же финалом. Однако может случиться и так, что процесс во время замены исполняемого файла будет «висеть» в блокирующем вызове, выполняя какой-нибудь select(), read() или waitpid(). В таком случае до окончания вызова с ним ничего не случится и он продолжит существовать. Вероятно, есть и другие сценарии выживания процесса после замены исполняемого файла, но я в силу небольшого опыта в linux с ними не знаком.

Мы выяснили, что происходит при перезаписи файла и как это может вызвать (или нет) падение процесса. Более надёжного способа перезапуска я так и не придумал, поэтому оставим все как есть. Тем более, работает этот способ все же достаточно хорошо (исключение — устройства samsung, у которых своя, самсунговская магия не даёт zygote упасть после перезаписи её исполняемого файла. Как это работает, я так и не выяснил).

Вернёмся к zygote. Что это за такой несущественный сервис, что я без проблем готов заменить его на какой-то другой бинарный файл? Zygote — это процесс, порождающий все android-приложения. Когда вы нажимаете на иконку того же clash of clans, zygote создаёт копию своего процесса с помощью fork(), и в этой копии запускает нужное приложение. Сделано это для экономии ресурсов и ускорения запуска. Если убить zygote, все android-приложения упадут вслед за ним. Такое положение вещей лишает наш тач логгер всякого смысла. Зачем собирать пользовательские жесты с кирпича? К счастью, эту проблему легко обойти.

Демоны


Во-первых, почему вдруг все android-приложения должны завершиться, если упадёт zygote? Ведь при завершении родительского процесса в linux дочерний продолжает прекрасно работать, а его новым родителем становится init. А дело в том, что zygote при запуске порождает новую группу процессов, или новую сессию. В этой сессии запускаются все android-приложения, все дочерние процессы андроид-приложений, их дочерние процессы и так далее. Когда завершается породивший сессию процесс, всё ветвистое дерево его потомков в той же сессии завершается вместе с ним. Так при закрытии эмулятора терминала завершатся все процессы, запущенные в нём. Вот почему zygote при падении обязательно утянет за собой в ад все android-приложения.

Как было упомянуто выше, это порождает некоторые проблемы. Первое. Кто вернёт app_process на место после того, как будет запущен payload, а наше приложение будет убито? И второе. Как оставить наш payload запущенным после возвращения app_process?

Чтобы процесс продолжил работать после завершения сессии, его необходимо отвязать от текущей сессии… Породив новую! Делается это с помощью системного вызова setsid(). Но можно поступить ещё хитрее. За один вызов можно запустить новый процесс, отвязать его от текущей сессии и отвязать его от стандартных потоков ввода\вывода. Этот волшебный вызов называется daemon() и он сочетает в себе мощь fork() и setsid(), порождая новый процесс-демон, который будет работать даже если родитель сгинет в аду вместе со всей своей сессией, ибо демон сам есть порождение ада.

C помощью daemon() прекрасно решаются обе упомянутые выше проблемы. Сначала мы создаём демона внутри приложения. Он переживёт падение zygote, поэтому он сможет перезаписать app_process на payload, дождаться его запуска, а затем вернуть app_process обратно.


Решение первой проблемы в картинках

Вторая решается аналогично: когда система запустит наш payload, мы создадим в ней другого демона, который будет работать и после восстановления app_process. Но будет ли? Падение процесса при перезаписи его исполняемого файла никто не отменял, от этого даже демонизация не спасает. От этого спасёт разве что замена исполняемого файла на лету. Так почему бы именно это и не сделать?

Execve()



Суть execve()

Этот системный вызов подменяет текущий процесс новым, запуская бинарный файл, указанный в аргументах. Подробнее прочитать об этом можно в соответствующем man.
Кстати, практически все процессы в linux запускаются связкой fork()+execve().

В итоге, система запускает наш payload. Мы внутри него делаем daemon() и execve() для запуска другого бинаря, назовём его exec_payload. Затем мы возвращаем app_process на место, но exec_payload продолжает работать.


Решение второй проблемы в картинках

Теперь мы знаем, как из обычного приложения запустить процесс от имени root. Точнее, как, убив половину системы, заставить ее выполнить наш код от имени root и вернуть всё ”как было”. Однако на этом трудности не заканчиваются. Помните про SELinux? Так вот, он никуда не делся и по-прежнему не разрешает выполнение многих операций.

SELinux[2]


Рассмотрим, в какой ситуации окажется наш exec_payload после запуска с точки зрения SELinux контекстов. У zygote контекст одноимённый — zygote. Exec_payload при запуске его наследует. У символьных устройств в /dev/input тоже есть свой контекст: input_device. И, ожидаемо, SELinux не разрешает процессам с контекстом zygote получать доступ к файлам с контекстом input_device. Неужели мы проделали весь этот путь только для того, чтобы снова упереться в SELinux?

Не совсем. На этот раз ситуация всё же немного лучше, и прав у нас побольше. Помните, что zygote запускает все android-приложения? Так вот, если у процесса с контекстом zygote рождается потомок с контекстом untrusted_app (или platform_app для встроенных приложений, или isolated_app… Кто бы знал для чего), это значит, что у zygote есть права менять selinux-контекст. Осталось найти нужный контекст, который имеет доступ к устройствам ввода. И найти его несложно: это shell.

Shell — это пользователь (обладающий одноимённым контекстом), от имени которого выполняются все команды, если зайти на android-устройство удалённо по ADB (Android debug bridge). У него прав поменьше, чем у root с контекстом init или kernel, но значительно больше, чем у android-приложений. В том числе, пользователь shell имеет доступ к устройствам ввода на чтение и запись. Поменяв контекст с zygote на shell и добавив себя в группу input, наш exec_payload наконец-то сможет получить доступ к вожделенным устройствам ввода.

Получение контекста shell из zygote
#ifdef __aarch64__
void * selinux = dlopen("/system/lib64/libselinux.so", RTLD_LAZY);
#else
void* selinux = dlopen("/system/lib/libselinux.so", RTLD_LAZY);
#endif

if (selinux)
{
  void* getcon = dlsym(selinux, "getcon");
  const char* error = dlerror();
  if (!error)
  {
    getcon_t* getcon_p = (getcon_t*) getcon;
    char* secontext;
    int ret = (*getcon_p)(&secontext);

    void* setcon = dlsym(selinux, "setcon");
    const char* error = dlerror();
    if (!error)
    {
      setcon_t* setcon_p = (setcon_t*) setcon;
      if ((*setcon_p)("u:r:shell:s0") != 0)
      {
        LOGV("Unable to set context: %s!", strerror(errno));
      }

      (*getcon_p)(&secontext);
      LOGV("Current context: %s", secontext);
    }
  }
  dlclose(selinux);
}
else
{
  LOGV("SELinux not found.");
}


И, наконец, осталось решить последнюю задачу. Как обмениваться данными между нашим (вновь запущенным после падения половины системы) приложением и демоном, читающим данные из /dev/input/event[X]? Возможно несколько вариантов. К сожалению, самые адекватные из них (UNIX socket, FIFO) недоступны нам (коварный SELinux!). Остаётся писать данные на SD-карту одним процессом и читать их оттуда другим процессом. Не самый крутой вариант, но всё же. Помимо этого, можно отправлять данные в приложение через специальные intent с помощью встроенной утилиты am (по сути, это командный интерфейс для Activity Manager). Возможно, есть другие способы, до которых я не додумался.

«Ручной» способ сбора данных


Использовать эксплойты для достижения различных целей — очень ненадёжно. Редко удаётся реализовать универсальное решение на основе эксплойта, которое будет работать на всех устройствах с любыми версиями Android. Моя реализация тач-логгера — не исключение. Не везде упадёт zygote после перезаписи app_process. Вероятно, не на всех устройствах SELinux позволит сменить контекст с zygote на shell нашему payload. И обязательно существуют и другие подводные камни, пока мне неизвестные.

Однако наша цель — собрать события ввода, и если тестовые пользователи согласны предоставить свои устройства для этого, можно внедрить ещё одно решение, простое и на 100% универсальное. Правда, для его запуска придётся проделать несколько ручных операций. Если закинуть exec_payload по adb в директорию /data/local/tmp (куда у shell есть доступ), и оттуда запустить его — демон начнёт работать точно так же, как если бы его запустило наше приложение. Единственное, будет отличаться UID (2000 вместо 0), но на доступе к устройствам ввода это никак не отразится. Так что в третьей версии своего тач-логгера (которая сейчас доводится до ума) я собираюсь предусмотреть и такой вариант в качестве крайней меры, оставив при этом и возможность запуска на рутованных устройствах как ещё один запасной вариант.

Другие применения DirtyCOW на Android


Понятное дело, не всегда (почти никогда) эксплойты не используются в таких благородных целях, как наука. И вам, наверное, интересно, какое ещё применение можно найти DirtyCOW, используя описанный выше способ повышения привилегий. Как вы понимаете, это не полноценный root, поэтому наши возможности весьма ограничены. Тем не менее, натворить бед можно и с этими правами. Первое: можно использовать доступ к устройствам ввода для похищения паролей (в тач-логгере я сделал кнопку «паузы» чтобы эти данные не попадали в мои и потенциально в чужие руки). Второе: можно использовать встроенные утилиты вроде am и pm. Это позволяет скрытно устанавливать и удалять приложения. Am в свою очередь позволяет получить информацию об activity, запущенной в данный момент поверх остальных. Идеальный функционал для банковского трояна, который при запуске банковского приложения будет рисовать поверх собственную форму логина. В общем, dirtyCOW, как и любой инструмент, можно использовать в разных целях, как в хороших, так и в не очень.

Один последний трюк


Напоследок хочу рассказать о самом интересном: ещё об одном способе выполнить ваш код от более привилегированного пользователя. Он позволит запускать код с привилегиями mediaserver, netd, debuggerd или других сервисов, если вам это вдруг понадобится.

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

Суть трюка очень проста и изящна. Практически у любого исполняемого файла в Android есть зависимость от системной библиотеки libcutils.so. И при запуске исполняемого файла эта библиотека будет подгружена в память процесса. Особенность разделяемых библиотек в формате ELF в том, что в них есть специальные секции .init и .init_array. Функции, адреса которых будут помещены в эти секции, выполнятся при подгрузке библиотеки в процесс. Соответственно, если мы скомпилируем библиотеку с функцией в секции .init_array и перезапишем ей файл /system/lib/libcutils.so, то при его подгрузке в процесс эта функция благополучно выполнится с привилегиями процесса.

__attribute__((constructor)) void say_hello()
{
  payload_main();
}

Функция с атрибутом constructor при компиляции помещается в секцию .init_array библиотеки.

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

DT_SONAME -> DT_NEEDED


У каждой библиотеки в формате ELF есть заголовок, в котором находится вся нужная линковщику информация о библиотеке. Нас интересует информация о её зависимостях и о её имени. Узнать всё это можно, например, с помощью команды objdump -p:

Динамический раздел:
[.....]
  STRTAB               0x00001660
  STRSZ                0x000014ec
  GNU_HASH             0x00002b4c
  NEEDED               liblog.so
  NEEDED               libc++.so
  NEEDED               libdl.so
  NEEDED               libc.so
  NEEDED               libm.so
  SONAME               libcutils.so
  FINI_ARRAY           0x0000fbf0
[.....]

Так выглядит фрагмент заголовка libcutils.so. Поля NEEDED содержат зависимости, которые будут подгружены линковщиком в тот же процесс, что и сам libcutils. Обратите внимание на поле SONAME, содержащее имя библиотеки. Примечательно, что оно не нужно линковщику для нормальной работы, он ищет библиотеки по именам их файлов. Так почему бы не разобрать ELF-заголовок и не вставить вместо этого ненужного поля ещё одну зависимость? От какой-нибудь менее важной системной библиотеки? Здесь нужно быть осторожным: длина имени новой зависимости не должна превышать длину имени самой библиотеки (поэтому я выбрал libcutils, а не, например, libc). К счастью, на примете есть отличный кандидат: libmtp.so. Она большого размера (несколько десятков килобайт), с коротким именем, и нужна только при подключении к компьютеру по USB для передачи файлов по протоколу MTP. То есть 2 минуты без неё прожить можно. Остальное — дело техники. Парсим ELF файл, находим поле с именем библиотеки, меняем тип поля с DT_SONAME (0xE) на DT_NEEDED (0x1), и само имя меняем на libmtp.so. Готово! У libcutils.so появляется новая зависимость:

Динамический раздел:
[.....]
  STRTAB               0x00001660
  STRSZ                0x000014ec
  GNU_HASH             0x00002b4c
  NEEDED               liblog.so
  NEEDED               libc++.so
  NEEDED               libdl.so
  NEEDED               libc.so
  NEEDED               libm.so
  NEEDED               libmtp.so
  FINI_ARRAY           0x0000fbf0
[.....]

Теперь дело за малым: перезаписать libmtp.so по нашему усмотрению и перезапустить процесс, от имени которого мы хотим выполнить код. Сделать это можно, опять же, уронив zygote, так как вслед за ней падают не только android-приложения, но и многие системные сервисы.
Стоит сказать спасибо авторам вируса Android.Loki.28.origin, у которых я почерпнул эту замечательную идею с внедрением зависимости в библиотеку.

Вместо заключения


Прошли времена, когда эксплойты под Android использовали вопиющие уязвимости в безопасности ОС, что позволяло безнаказанно творить в системе всё, что угодно. Сегодня Android хорошо защищён, и немалый вклад в защиту внёс SELinux, появившийся в версии 4.3, и включённый по умолчанию в 5.0. Тем не менее, лазейки в защите всё ещё встречаются, и они всё ещё повышают привилегии настолько, чтобы их можно было использовать на практике, как в хороших целях, так и во зло. Надеюсь, вам было интересно узнать о том, как одному из самых, казалось бы, бесполезных эксплойтов в Android удалось найти полезное применение. И надеюсь, что новая версия тач-логгера поможет мне собрать вагоны ценнейших пользовательских данных и продвинет исследование поведенческих признаков жестов на сенсорном экране вперёд.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332916/


Метки:  

27000 ошибок в операционной системе Tizen

Среда, 12 Июля 2017 г. 11:13 + в цитатник
PVS-Studio and Tizen

Эта статья продемонстрирует, что при разработке крупных проектов статический анализ кода является не просто полезным, а совершенно необходимым элементом процесса разработки. Я начинаю цикл статей, посвященных возможности использования статического анализатора кода PVS-Studio для повышения качества и надежности операционной системы Tizen. Для начала я проверил небольшую часть операционной системы (3.3%) и выписал около 900 предупреждений, указывающих на настоящие ошибки. Если экстраполировать результаты, то получается, что наша команда способна выявить и устранить в Tizen около 27000 ошибок. По итогам проведённого исследования я подготовил презентацию, которая предназначалась для демонстрации представителям Samsung и была посвящена возможному сотрудничеству. Встреча перенесена на неопределённый срок, поэтому я решил не тратить время и трансформировать материал презентации в статью. Запасайтесь вкусняшками и напитками, нас ждёт длинный программистский триллер.

Начать, пожалуй, следует со ссылки на презентацию «Как команда PVS-Studio может улучшить код операционной системы Tizen», из которой и родилась эта статья: RU — pptx, slideshare; EN — pptx, slideshare. Впрочем, смотреть презентацию вовсе не обязательно, так как весь содержащийся в ней материал будет рассмотрен здесь, причём более подробно. Тема презентации пересекается с открытым письмом, в котором мы также рассказываем, что готовы поработать с проектом Tizen.

С разными воспоминаниями и ссылками я закончил и перехожу к сути дела. Первое, что стоит сделать, это напомнить читателю, что вообще из себя представляет операционная система Tizen.

Tizen


Tizen — открытая операционная система на базе ядра Linux, предназначенная для широкого круга устройств, включая смартфоны, интернет-планшеты, компьютеры, автомобильные информационно-развлекательные системы, «умные» телевизоры и цифровые камеры, разрабатываемая и управляемая такими корпорациями, как Intel и Samsung. Поддерживает аппаратные платформы на процессорах архитектур ARM и x86. Более подробная информация доступна на сайте Wikipedia.

Платформа Tizen показывает уверенный рост в течение нескольких последних лет, несмотря на насыщенность рынка операционных систем для мобильных и носимых устройств. Согласно отчёту Samsung, рост продаж мобильных телефонов с OC Tizen составил 100% в 2017 году.

Tizen


Для нашей команды операционная система Tizen привлекательна тем, что компания Samsung заинтересована в её надёжности и прикладывает усилия для улучшения качества кода. Например, с целью улучшения качества кода, Samsung инвестировал разработку в ИСП РАН специализированного анализатора кода Svace. Инструмент Svace используется как основное средство для обеспечения безопасности системного и прикладного ПО платформы Tizen. Вот некоторые цитаты, взятые из заметки «Samsung have Invested $10 Million in Svace, Security Solution to Analyze Tizen Apps»:

As part of its security measures, Samsung are using the SVACE technology (Security Vulnerabilities and Critical Errors Detector) to detect potential vulnerabilities and errors that might exist in source code of applications created for the Tizen Operating System (OS). This technology was developed by ISP RAS (Institute for System Programming of the Russian Academy of Sciences), who are based in Moscow, Russia.

The solution is applied as part of the Tizen Static Analyzer tool that is included in the Tizen SDK and Studio. Using this tool you can perform Static security analysis of the Tizen apps native C / C ++ source code and discover any issues that they might have. The tool helps discover a wide range of issues at compilation time, such as the dereference of Null Pointers, Memory Leaks, Division by Zero, and Double Free etc.

Также стоит отметить новость «В России впервые создали защищенную операционную систему», опубликованную на сайте rbc.ru. Цитата:

О создании российской версии операционной системы (ОС) Tizen было объявлено 2 июня 2016. Tizen является независимой ОС, то есть она не привязана к облаку Apple или Google, и поэтому ее можно адаптировать под локальные рынки, рассказал президент ассоциации «Тайзен.ру» Андрей Тихонов. Основная особенность представленной ОС — в ее безопасности, уточнил Тихонов. Это первая и единственная в России сертифицированная в Федеральной службе по техническому и экспортному контролю (ФСТЭК) мобильная операционная система, уточнил представитель Samsung.

Команда PVS-Studio просто не могла пройти мимо такого интересного открытого проекта, не попробовав его проверить.

Анализ Tizen


Целью презентации, о которой я упоминал ранее, было продемонстрировать, что анализатор PVS-Studio находит очень много ошибок различного типа. Это своего рода резюме анализатора и нашей команды, которое мы хотим показать компании Samsung.

Читатель может засомневаться, стоит ли ему читать такую «статью-резюме». Да, стоит, здесь будет много интересного и полезного. Во-первых, лучше учиться на чужих ошибках, а не на своих. Во-вторых, статья отлично показывает, что методологию статического анализа просто необходимо применять в проектах большого размера. Если кто-то из коллег, занятых в большом проекте, уверяет вас, что они пишут код высокого качества и почти без ошибок, то покажите ему эту статью. Я не думаю, что создатели Tizen хотели, чтобы в проекте было много дефектов, но вот они — тысячи багов.

Как всегда, хочу напомнить, что статический анализ кода должен применяться регулярно. Разовая проверка Tizen или любого другого проекта, конечно, будет полезна, но малоэффективна. В основном будут найдены малозначительные ошибки, которые слабо оказывают влияние на работоспособность проекта. Все явные ошибки были выявлены другими методами, например, благодаря жалобам пользователей. Означает ли это, что толку от статического анализа мало? Конечно нет, просто, как я уже сказал, разовые проверки — это неэффективный способ использования анализаторов кода. Анализаторы должны использоваться регулярно и тогда многие, в том числе и критические, ошибки будут обнаруживаться на самом раннем этапе. Чем раньше ошибка выявлена, тем дешевле её исправить.

Я считаю, что:
  • Сейчас анализатор PVS-Studio выявляет более 10% ошибок, которые присутствуют в коде проекта Tizen.
  • В случае регулярного использования PVS-Studio в новом коде можно будет предотвратить около 20% ошибок.
  • Я прогнозирую, что команда PVS-Studio может на данный момент выявить и исправить около 27000 ошибок в проекте Tizen.

Конечно, я могу ошибаться, но я не подгонял результаты исследования, чтобы показать возможности анализатора PVS-Studio в лучшем свете. Это просто не нужно. Анализатор PVS-Studio — мощный инструмент, который находит так много дефектов, что просто нет смысла фальсифицировать результаты. Далее я продемонстрирую, как получились все приведённые здесь числа.

Конечно, я не мог проверить весь проект Tizen. Вместе со сторонними библиотеками он насчитывает 72 500 000 строк C и C++ кода (без учёта комментариев). Поэтому я решил случайным образом проверить несколько десятков проектов, входящих в состав Tizen:Unified (https://build.tizen.org/project/show/Tizen:Unified).

Выбирая проекты, я делил их на две группы. Первая группа — это проекты, написанные непосредственно сотрудниками компании Samsung. Я определял такие проекты по наличию в начале файлов комментариев, выглядящих вот так:
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
....
*/

Вторая группа — это сторонние проекты, используемые в проекте Tizen. Впрочем, многие проекты нельзя считать полностью сторонними, так как в них присутствуют различные патчи. Вот пример патча, сделанного в библиотеке efl-1.16.0:
//TIZEN_ONLY(20161121)
// Pre-rotation should be enabled only when direct
// rendering is set but client side rotation is not set
if ((sfc->direct_fb_opt) &&
    (!sfc->client_side_rotation) &&
    (evgl_engine->funcs->native_win_prerotation_set))
  {
    if (!evgl_engine->funcs->native_win_prerotation_set(eng_data))
      ERR("Prerotation does not work");
  }
//

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

Выбранные случайным образом проекты были проверены, и я приступил к изучению отчётов анализатора и выбору предупреждений, которые заслуживают внимания. Конечно, многие ошибки достаточно безобидны или могут проявлять себя крайне редко. Например, следующий код будет давать сбой очень-очень редко:
m_ptr = (int *)realloc(m_ptr, newSize);
if (!m_ptr) return ERROR;

Если не удастся выделить новый фрагмент памяти, то произойдёт утечка памяти. Подробнее этот тип ошибки будет рассмотрен далее. Да, вероятность утечки памяти крайне мала, но, на мой взгляд, это именно ошибка, которую следует исправить.

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

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

Один из разработчиков, посмотрев презентацию и не разобравшись что к чему, написал комментарий вида «27000 предупреждений анализатора, тоже мне достижение, это даже мало». Поэтому ещё раз подчеркну, что речь идет именно об ошибках. В процессе исследования я считал и выписывал именно ошибки, а не просто все предупреждения анализатора без разбора.

Анализ проектов, разработанных специалистами компании Samsung


Для проверки я случайным образом выбрал следующие проекты: bluetooth-frwk-0.2.157, capi-appfw-application-0.5.5, capi-base-utils-3.0.0, capi-content-media-content-0.3.10, capi-maps-service-0.6.12, capi-media-audio-io-0.3.70, capi-media-codec-0.5.3, capi-media-image-util-0.1.15, capi-media-player-0.3.58, capi-media-screen-mirroring-0.1.78, capi-media-streamrecorder-0.0.10, capi-media-vision-0.3.24, capi-network-bluetooth-0.3.4, capi-network-http-0.0.23, cynara-0.14.10, e-mod-tizen-devicemgr-0.1.69, ise-engine-default-1.0.7, ise-engine-sunpinyin-1.0.10, ise-engine-tables-1.0.10, isf-3.0.186, org.tizen.app-selector-0.1.61, org.tizen.apps-0.3.1, org.tizen.bluetooth-0.1.2, org.tizen.browser-3.2.0, org.tizen.browser-profile_common-1.6.4, org.tizen.classic-watch-0.0.1, org.tizen.d2d-conv-setting-profile_mobile-1.0, org.tizen.d2d-conv-setting-profile_wearable-1.0, org.tizen.download-manager-0.3.21, org.tizen.download-manager-0.3.22, org.tizen.dpm-toolkit-0.1, org.tizen.elm-demo-tizen-common-0.1, org.tizen.indicator-0.2.53, org.tizen.inputdelegator-0.1.170518, org.tizen.menu-screen-1.2.5, org.tizen.myplace-1.0.1, org.tizen.privacy-setting-profile_mobile-1.0.0, org.tizen.privacy-setting-profile_wearable-1.0.0, org.tizen.quickpanel-0.8.0, org.tizen.screen-reader-0.0.8, org.tizen.service-plugin-sample-0.1.6, org.tizen.setting-1.0.1, org.tizen.settings-0.2, org.tizen.settings-adid-0.0.1, org.tizen.telephony-syspopup-0.1.6, org.tizen.voice-control-panel-0.1.1, org.tizen.voice-setting-0.0.1, org.tizen.volume-0.1.149, org.tizen.w-home-0.1.0, org.tizen.w-wifi-1.0.229, org.tizen.watch-setting-0.0.1, security-manager-1.2.17.

Проектов немало, но многие из них имеют совсем маленький размер. Давайте посмотрим, какие типы ошибок мне встретились.

Примечание. Помимо предупреждения PVS-Studio я буду стараться классифицировать найденные ошибки согласно CWE (Common Weakness Enumeration). Однако, я вовсе не делаю попытку найти какие-то уязвимости, а привожу CWE-ID исключительно для удобства тех читателей, которые привыкли именно к этой классификации дефектов. Моя основная цель — найти как можно больше ошибок, а определение того, насколько опасна ошибка с точки зрения безопасности, выходит за рамки моего исследования.

Это будет большой раздел, поэтому предлагаю заварить первую кружку чая или кофе. Вторая нам понадобится позже, так как мы только в самом начале статьи.

Чай, кофе


Опечатка в условии: слева и справа одно и то же (2 ошибки)


Классика. Даже так: классика самого высшего сорта!

Во-первых, эта ошибка выявляется с помощью диагностики V501. Диагностика выявляет опечатки и последствия неудачного Copy-Paste. Это невероятно популярный и распространенный тип ошибок. Посмотрите сами, какую замечательную коллекцию багов в открытых проектах мы собрали благодаря диагностике V501.

Во-вторых, эта ошибка находится в операторе меньше (< ). Неправильное сравнение двух объектов — это тоже классическая ошибка, возникающая из-за того, что никто не проверяет такие простые функции. Недавно я написал интересную статью на эту тему "Зло живёт в функциях сравнения". Почитайте, это своего рода «Хребты безумия» для программистов.

Код, о котором идёт речь:
bool operator <(const TSegment& other) const {
  if (m_start < other.m_start)
    return true;
  if (m_start == other.m_start)
    return m_len < m_len;                // <=
  return false;
}

Ошибка найдена благодаря предупреждению PVS-Studio: V501 There are identical sub-expressions to the left and to the right of the '<' operator: m_len < m_len segmentor.h 65

Software weaknesses type — CWE-570: Expression is Always False

Из-за этой ошибки объекты, которые отличаются только значением члена m_len, будут сравниваться некорректно. Правильный вариант сравнения:
return m_len < other.m_len;

Аналогичная ошибка: V501 There are identical sub-expressions '0 == safeStrCmp(btn_str, setting_gettext(«IDS_ST_BUTTON_OK»))' to the left and to the right of the '||' operator. setting-common-general-func.c 919

Опечатка в условии: бессмысленное сравнение (2 ошибки)


static void __page_focus_changed_cb(void *data)
{
  int i = 0;
  int *focus_unit = (int *)data;
  if (focus_unit == NULL || focus_unit < 0) {    // <=
    _E("focus page is wrong");
    return ;
  }
  ....
}

Ошибка найдена благодаря предупреждению PVS-Studio: V503 This is a nonsensical comparison: pointer < 0. apps_view_circle_indicator.c 193

Software weaknesses type — CWE-697: Insufficient Comparison

Сравнение вида «pointer < 0» не имеет смысла и свидетельствует об опечатке в коде. Как я понимаю, пропущен оператор звездочка (*), который должен был разыменовать указатель. Корректный код:
if (focus_unit == NULL || *focus_unit < 0) {

Этот код вместе с ошибкой скопировали, в результате чего точно такую же ошибку можно наблюдать в функции __page_count_changed_cb:
static void __page_count_changed_cb(void *data)
{
  int i = 0;
  int *page_cnt = (int *)data;
  if (page_cnt == NULL || page_cnt < 0) {
    _E("page count is wrong");
    return ;
  }
  ....
}

Ох уж эта Copy-Paste технология. Для этого кода анализатор выдал предупреждение: V503 This is a nonsensical comparison: pointer < 0. apps_view_circle_indicator.c 219

Опасный способ использования функции alloca (1 ошибка)


Рассмотрим код, который хотя и плох, но на практике не приведёт к проблемам. Я не рассматривал эту ситуацию в презентации, так как она требует специального пояснения. Теперь же, в статье, я могу остановиться на данном примере и поделиться своими мыслями.
int audio_io_loopback_in_test()
{
  ....
  while (1) {
    char *buffer = alloca(size);
    if ((ret = audio_in_read(input, (void *)buffer, size)) >
        AUDIO_IO_ERROR_NONE)
    {
      fwrite(buffer, size, sizeof(char), fp);
      printf("PASS, size=%d, ret=0x%x\n", size, ret);
    } else {
      printf("FAIL, size=%d, ret=0x%x\n", size, ret);
    }
  }
  ....
}

Предупреждение PVS-Studio: V505 The 'alloca' function is used inside the loop. This can quickly overflow stack. audio_io_test.c 247

Software weaknesses type — CWE-770: Allocation of Resources Without Limits or Throttling

В цикле, который выполняется, пока не закончится аудио-поток, происходит выделение стековой памяти с помощью функции alloca. Такой код плох тем, что очень быстро может исчерпаться стековая память.

Однако я не могу сказать, что нашел серьезную ошибку. Дело в том, что этот код относится к тестам. Уверен, что звуковой поток в тестах короткий и никаких проблем при его обработке в тестах не возникает.

Таким образом, не совсем честно говорить, что это именно ошибка, ведь тесты работают.

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

Более того, код можно легко исправить, а значит — это надо сделать. Достаточно вынести выделение памяти из цикла. Это можно сделать, так как размер выделяемого буфера не изменяется.

Хороший код:
char *buffer = alloca(size);
while (1) {
  if ((ret = audio_in_read(input, (void *)buffer, size)) >
      AUDIO_IO_ERROR_NONE)
  {
    fwrite(buffer, size, sizeof(char), fp);
    printf("PASS, size=%d, ret=0x%x\n", size, ret);
  } else {
    printf("FAIL, size=%d, ret=0x%x\n", size, ret);
  }
}

Используется уже несуществующий буфер (1 ошибка)


Следующий код также относится к тестам, но ошибка в нем гораздо серьезнее. Из-за ошибки возникает неопределённое поведение программы, поэтому доверять такому тесту совершенно нельзя. Другими словами — тест ничего не тестирует.
void extract_input_aacdec_m4a_test(
  App * app, unsigned char **data, int *size, bool * have_frame)
{
  ....
  unsigned char buffer[100000];
  ....
DONE:
  *data = buffer;
  *have_frame = TRUE;
  if (read_size >= offset)
    *size = offset;
  else
    *size = read_size;
}

Ошибка найдена благодаря предупреждению PVS-Studio: V507 Pointer to local array 'buffer' is stored outside the scope of this array. Such a pointer will become invalid. media_codec_test.c 793

Software weaknesses type — CWE-562: Return of Stack Variable Address

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

Обрабатывается меньше или больше элементов массива, чем требуется (7 ошибок)


Для начала рассмотрим случай, когда обрабатывается меньше элементов, чем требуется.
typedef int gint;
typedef gint gboolean;  

#define BT_REQUEST_ID_RANGE_MAX 245

static gboolean req_id_used[BT_REQUEST_ID_RANGE_MAX];

void _bt_init_request_id(void)
{
  assigned_id = 0;
  memset(req_id_used, 0x00, BT_REQUEST_ID_RANGE_MAX);
}

Предупреждение PVS-Studio: V512 A call of the 'memset' function will lead to underflow of the buffer 'req_id_used'. bt-service-util.c 38

Software weaknesses type — CWE-131: Incorrect Calculation of Buffer Size

Здесь программист забыл, что в качестве третьего аргумента функция memset принимает размер буфера в байтах, а не количество элементов массива. Не зря я в своё время назвал memset самой опасной функцией в мире программирования на C/C++. Эта функция продолжает сеять хаос в различных проектах.

Тип gboolean занимает не 1 байт, а 4. В результате будет обнулена только 1/4 часть массива, а остальные элементы останутся неинициализированными.

Правильный вариант кода:
memset(req_id_used, 0x00, BT_REQUEST_ID_RANGE_MAX * sizeof(gboolean));

Или можно написать так:
memset(req_id_used, 0x00, sizeof(req_id_used));

Теперь рассмотрим случай, когда может произойти выход за границу массива.
static void _on_atspi_event_cb(const AtspiEvent * event)
{
  ....
  char buf[256] = "\0";
  ....
  snprintf(buf, sizeof(buf), "%s, %s, ",
           name, _("IDS_BR_BODY_IMAGE_T_TTS"));
  ....
  snprintf(buf + strlen(buf), sizeof(buf),
           "%s, ", _("IDS_ACCS_BODY_SELECTED_TTS"));
  ....
}

Предупреждение PVS-Studio: V512 A call of the 'snprintf' function will lead to overflow of the buffer 'buf + strlen(buf)'. app_tracker.c 450

Software weaknesses type — CWE-131: Incorrect Calculation of Buffer Size

Надежная операционная система… Эх…

Обратите внимание, что второй вызов snprintf должен добавить что-то к уже имеющейся строке. Поэтому адресом буфера является выражение buf + strlen(buf). При этом функция имеет право печатать символов меньше, чем размер буфера. Из размера буфера надо вычесть strlen(buf). Но про это забыли и может возникнуть ситуация, когда функция snprintf запишет данные за границу массива.

Корректный код:
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
         "%s, ", _("IDS_ACCS_BODY_SELECTED_TTS"));

Третий фрагмент кода демонстрирует ситуацию, когда выход за границу происходит всегда. Для начала рассмотрим некоторые структуры.
#define BT_ADDRESS_STRING_SIZE 18

typedef struct {
 unsigned char addr[6];
} bluetooth_device_address_t;

typedef struct {
 int count;
 bluetooth_device_address_t addresses[20];
} bt_dpm_device_list_t;

Здесь нам важно, что массив addr состоит из 6 элементов. Запомните этот размер, а также, что макрос BT_ADDRESS_STRING_SIZE раскрывается в константу 18.

Теперь собственно некорректный код:
dpm_result_t _bt_dpm_get_bluetooth_devices_from_whitelist(
  GArray **out_param1)
{
  dpm_result_t ret = DPM_RESULT_FAIL;
  bt_dpm_device_list_t device_list;
  ....
  for (; list; list = list->next, i++) {
    memset(device_list.addresses[i].addr, 0, BT_ADDRESS_STRING_SIZE);
    _bt_convert_addr_string_to_type(device_list.addresses[i].addr,
                                    list->data);
  }
  ....
}

Предупреждение PVS-Studio: V512 A call of the 'memset' function will lead to overflow of the buffer 'device_list.addresses[i].addr'. bt-service-dpm.c 226

Software weaknesses type — CWE-805: Buffer Access with Incorrect Length Value

Выделю самое главное:
memset(device_list.addresses[i].addr, 0, BT_ADDRESS_STRING_SIZE);

Итак, как мы видели раньше, размер addr всего 6 байт. При этом функция memset обнуляет 18 байт и, как следствие, происходит выход за границу массива.

Ещё 4 найденные ошибки:
  • V512 A call of the 'memset' function will lead to overflow of the buffer 'device_list.addresses[i].addr'. bt-service-dpm.c 176
  • V512 A call of the 'memset' function will lead to underflow of the buffer 'formatted_number'. i18ninfo.c 544
  • V512 A call of the 'snprintf' function will lead to overflow of the buffer 'ret + strlen(ret)'. navigator.c 677
  • V512 A call of the 'snprintf' function will lead to overflow of the buffer 'trait + strlen(trait)'. navigator.c 514

Логическая ошибка в последовательностях if… else… if (4 ошибки)


char *voice_setting_language_conv_lang_to_id(const char* lang)
{
  ....
  } else if (!strcmp(LANG_PT_PT, lang)) {
    return "pt_PT";
  } else if (!strcmp(LANG_ES_MX, lang)) {     // <=
    return "es_MX";
  } else if (!strcmp(LANG_ES_US, lang)) {     // <=
    return "es_US";
  } else if (!strcmp(LANG_EL_GR, lang)) {
    return "el_GR";
  ....
}

Предупреждение PVS-Studio: V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 144, 146. voice_setting_language.c 144

Software weaknesses type — CWE-570 Expression is Always False

Рассматривая этот код, нельзя понять, в чём же заключается ошибка. Всё дело в том, что LANG_ES_MX и LANG_ES_US — это идентичные строки. Вот они:
#define LANG_ES_MX "\x45\x73\x70\x61\xC3\xB1\x6f\x6c\x20\x28\" \
 "x45\x73\x74\x61\x64\x6f\x73\x20\x55\x6e\x69\x64\x6f\x73\x29"

#define LANG_ES_US "\x45\x73\x70\x61\xC3\xB1\x6f\x6c\x20\x28\" \
 "x45\x73\x74\x61\x64\x6f\x73\x20\x55\x6e\x69\x64\x6f\x73\x29"

Как я понимаю, эти строки на самом деле должны отличаться. Но поскольку строки одинаковые, то второе условие всегда ложно и функция никогда не вернёт значение «es_US».

Примечание. ES_MX — это Spanish (Mexico), ES_US — это Spanish (United States).

Эту ошибку я нашел в проекте org.tizen.voice-setting-0.0.1. Что интересно, вновь подводит технология Copy-Paste, и точно такую же ошибку я встретил в проекте org.tizen.voice-control-panel-0.1.1.

Другие ошибки:
  • V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 144, 146. voice_setting_language.c 144
  • V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 792, 800. setting-common-general-func.c 792
  • V517 The use of 'if (A) {...} else if (A) {...}' pattern was detected. There is a probability of logical error presence. Check lines: 801, 805. setting-common-general-func.c 801

Повторное присваивание (11 ошибок)


Рассмотрим ошибку в логике работы программы. Разработчик хотел обменять значения двух переменных, но запутался и у него получился следующий код:
void
isf_wsc_context_del (WSCContextISF *wsc_ctx)
{
  ....
  WSCContextISF* old_focused = _focused_ic;
  _focused_ic = context_scim;
  _focused_ic = old_focused;
  ....
}

Предупреждение PVS-Studio: V519 The '_focused_ic' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1260, 1261. wayland_panel_agent_module.cpp 1261

Software weaknesses type — CWE-563 Assignment to Variable without Use ('Unused Variable')

Переменной _focused_ic два раза подряд присваиваются разные значения. Правильный код должен быть таким:
WSCContextISF* old_focused = _focused_ic;
_focused_ic = context_scim;
context_scim = old_focused;

Впрочем, в таких случаях лучше использовать функцию std::swap. Так намного меньше шансов ошибиться:
std::swap(_focused_ic, context_scim);

Рассмотрим другой вариант ошибки, которая возникла при написании однотипного кода. Возможно, в её появлении виноват метод Copy-Paste.
void create_privacy_package_list_view(app_data_s* ad)
{
  ....
  Elm_Genlist_Item_Class *ttc = elm_genlist_item_class_new();
  Elm_Genlist_Item_Class *ptc = elm_genlist_item_class_new();
  Elm_Genlist_Item_Class *mtc = elm_genlist_item_class_new();
  ....
  ttc->item_style = "title";
  ttc->func.text_get = gl_title_text_get_cb;
  ttc->func.del = gl_del_cb;                   // <=

  ptc->item_style = "padding";
  ptc->func.del = gl_del_cb;

  mtc->item_style = "multiline";
  mtc->func.text_get = gl_multi_text_get_cb;
  ttc->func.del = gl_del_cb;                   // <=
  ....
}

Предупреждение PVS-Studio: V519 The 'ttc->func.del' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 409, 416. privacy_package_list_view.c 416

Software weaknesses type — CWE-563 Assignment to Variable without Use ('Unused Variable')

Во втором случае значение должно было присваиваться переменной mtc->func.del.

Другие ошибки:
  • V519 The 'item' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1176, 1189. w-input-stt-voice.cpp 1189
  • V519 The 'ad->paired_item' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1614, 1617. bt-main-view.c 1617
  • V519 The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 191, 194. main.c 194
  • V519 The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 372, 375. setting-about-status.c 375
  • V519 The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 91, 100. setting-applications-defaultapp.c 100
  • V519 The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 52, 58. smartmanager-battery.c 58
  • V519 The 'ret' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 853, 872. setting-time-main.c 872
  • V519 The 'ret_str' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2299, 2300. setting-password-sim.c 2300
  • V519 The 'display_status' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 110, 117. view_clock.c 117

При просмотре отчета анализатора я выделил только 11 мест, которые, на мой взгляд, следует исправить. Но на самом деле сообщений V519 было намного больше. Очень часто они относились к коду, когда в переменную много раз подряд сохранялся результат после вызова функций. Речь идёт о коде вида:
status = Foo(0); 
status = Foo(1);
status = Foo(2);

Такой код возникает, как правило, в двух случаях:
  1. Результат вызова функций не важен, но какой-то компилятор или анализатор выдал предупреждение, от которого избавились, записав результат в переменную. По-моему мнению, тогда было бы лучше написать (void)Foo(x);.
  2. Результат вызова функций не важен, но иногда может пригодиться для отладки. Намного проще отлаживать код, когда результат записывается в какую-то переменную.

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

Разыменование (потенциально) нулевого указателя (всего ошибок 88)


Использование нулевых указателей выявляется с помощью диагностик V522 и V575. Предупреждение V522 выдаётся, кода происходит разыменование указателя, который может быть нулевым (*MyNullPtr = 2;). V575 — когда потенциально нулевой указатель передаётся в функцию, внутри которой он может быть разыменован (s = strlen(MyNullPtr);). На самом деле V575 выдаётся и для некоторых других случаев, когда используются некорректные аргументы, но на данный момент это нам не интересно. Сейчас, с точки зрения статьи, разницы между предупреждениями V522 и V575 нет, поэтому они будут рассмотрены в этой главе совместно.

Отдельно стоит поговорить о таких функциях, как malloc, realloc, strdup. Следует проверять, не вернули ли они NULL, если невозможно выделить блок памяти запрошенного размера. Впрочем, некоторые программисты придерживаются плохой практики и никогда сознательно не делают проверок. Их логика такова, раз нет памяти, то и не стоит дальше мучиться, пусть программа упадёт. Я считаю такой подход плохим, но он есть, и я не раз слышал доводы в его защиту.

К счастью, разработчики Tizen к упомянутой категории не относятся и обычно проверяют, удалось ли выделить память или нет. Иногда они это делают даже там, где не надо:
static FilterModule *__filter_modules = 0;
static void
__initialize_modules (const ConfigPointer &config)
{
  ....
  __filter_modules = new FilterModule [__number_of_modules];
  if (!__filter_modules) return;
  ....
}

В такой проверке нет смысла, так как в случае неудачи выделения памяти оператор new сгенерирует исключение типа std::bad_alloc. Впрочем, это совсем другая история. Я привел этот код исключительно для того, чтобы показать, что в проекте Tizen принято проверять, удалось ли выделить память.

Так вот, анализатор PVS-Studio обнаруживает, что во многих местах проверок не хватает. Здесь я рассмотрю только один случай, так как в общем-то они все однотипны.
void QuickAccess::setButtonColor(Evas_Object* button,
                                 int r, int g, int b, int a)
{
  Edje_Message_Int_Set* msg =
  (Edje_Message_Int_Set *)malloc(sizeof(*msg) + 3 * sizeof(int));
  msg->count = 4;
  msg->val[0] = r;
  msg->val[1] = g;
  msg->val[2] = b;
  msg->val[3] = a;
  edje_object_message_send(elm_layout_edje_get(button),
                           EDJE_MESSAGE_INT_SET, 0, msg);
  free(msg);
}

Предупреждение PVS-Studio: V522 There might be dereferencing of a potential null pointer 'msg'. QuickAccess.cpp 743

Software weaknesses type — CWE-690: Unchecked Return Value to NULL Pointer Dereference

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

Однако нулевые указатели могут возвращать не только функции, выделяющие память. Есть и другие ситуации, когда следует проверять указатель перед использованием. Рассмотрим пару таких примеров. Первый из них связан с небезопасным использованием оператора dynamic_cast.
int cpp_audio_in_peek(audio_in_h input, const void **buffer,
                      unsigned int *length) {
  ....
  CAudioInput* inputHandle = 
    dynamic_cast(handle->audioIoHandle);
  assert(inputHandle);
  inputHandle->peek(buffer, &_length);
  ....
}

Предупреждение PVS-Studio: V522 There might be dereferencing of a potential null pointer 'inputHandle'. cpp_audio_io.cpp 928

Software weaknesses type — CWE-690: Unchecked Return Value to NULL Pointer Dereference

Странный код. Если есть уверенность, что в handle->audioIoHandle хранится указатель на объект типа CAudioInput, то надо использовать static_cast. Если уверенности нет, то нужна проверка, так как макрос assert не поможет в release-версии.

Думаю, правильнее будет добавить вот такую проверку:
CAudioInput* inputHandle = 
  dynamic_cast(handle->audioIoHandle);
if (inputHandle == nullptr) {
  assert(false);
  THROW_ERROR_MSG_FORMAT(
     CAudioError::EError::ERROR_INVALID_HANDLE, "Handle is NULL");
}

Кстати, именно подобным образом сделано в других функциях. Так что анализатор действительно нашел дефект в программе.

Следующий код, возможно, и не приводит к настоящей ошибке. Допустим, сейчас всегда обрабатываются такие строки, в которых обязательно присутствуют символы '-' и '.'. Однако, я надеюсь вы согласитесь, что код опасен и лучше подстраховаться. Выбрал я его, чтобы продемонстрировать разнообразие ситуаций, когда анализатор выдаёт предупреждения.
int main(int argc, char *argv[])
{
  ....
  char *temp1 = strstr(dp->d_name, "-");
  char *temp2 = strstr(dp->d_name, ".");

  strncpy(temp_filename, dp->d_name,
          strlen(dp->d_name) - strlen(temp1));
  strncpy(file_format, temp2, strlen(temp2));
  ....
}

Предупреждения PVS-Studio:
  • V575 The potential null pointer is passed into 'strlen' function. Inspect the first argument. image_util_decode_encode_testsuite.c 207
  • V575 The potential null pointer is passed into 'strlen' function. Inspect the first argument. image_util_decode_encode_testsuite.c 208

Software weaknesses type — CWE-690: Unchecked Return Value to NULL Pointer Dereference

Указатели temp1 и temp2 могут оказаться нулевыми, если в строке не обнаружатся символы '-' и '.'. В этом случае чуть позже произойдёт разыменование нулевого указателя.

Есть ещё 84 участка кода, где разыменовываются указатели, которые могут оказаться равны NULL. Рассматривать их в статье нет никакого смысла. Нет смысла даже приводить их просто списком, так как это все равно займёт много места. Поэтому я выписал эти предупреждения в отдельный файл: Tizen_V522_V575.txt.

Одинаковые действия (6 ошибок)


static void _content_resize(...., const char *signal)
{
  ....
  if (strcmp(signal, "portrait") == 0) {
    evas_object_size_hint_min_set(s_info.layout,
      ELM_SCALE_SIZE(width), ELM_SCALE_SIZE(height));
  } else {
    evas_object_size_hint_min_set(s_info.layout,
      ELM_SCALE_SIZE(width), ELM_SCALE_SIZE(height));
  }
  ....
}

Предупреждение PVS-Studio: V523 The 'then' statement is equivalent to the 'else' statement. page_setting_all.c 125

Software weaknesses type — не знаю как классифицировать, буду благодарен за подсказку.

Вне зависимости от условия, выполняются одни и те же действия. Как я понимаю, в одном из двух вызовов функции evas_object_size_hint_min_set следует поменять местами width и height.

Рассмотрим ещё одну ошибку этого типа:
static Eina_Bool _move_cb(void *data, int type, void *event)
{
  Ecore_Event_Mouse_Move *move = event;

  mouse_info.move_x = move->root.x;
  mouse_info.move_y = move->root.y;

  if (mouse_info.pressed == false) {
    return ECORE_CALLBACK_RENEW;                 // <=
  }

  return ECORE_CALLBACK_RENEW;                   // <=
}

Предупреждение PVS-Studio: V523 The 'then' statement is equivalent to the subsequent code fragment. mouse.c 143

Software weaknesses type — CWE-393 Return of Wrong Status Code

Очень странно, что функция выполняет какую-то проверку, но всё равно всегда возвращает значение ECORE_CALLBACK_RENEW. Думаю, возвращаемые значения должны различаться.

Другие ошибки этого типа:
  • V523 The 'then' statement is equivalent to the 'else' statement. bt-httpproxy.c 383
  • V523 The 'then' statement is equivalent to the 'else' statement. streamrecorder_test.c 342
  • V523 The 'then' statement is equivalent to the 'else' statement. ise-stt-option.cpp 433
  • V523 The 'then' statement is equivalent to the subsequent code fragment. dpm-toolkit-cli.c 87

Забыли разыменовать указатель (1 ошибка)


Очень красивая ошибка: пишем данные неизвестно куда.
int _read_request_body(http_transaction_h http_transaction,
                       char **body)
{
  ....
  *body = realloc(*body, new_len + 1);
  ....
  memcpy(*body + curr_len, ptr, body_size);
  body[new_len] = '\0';                        // <=
  curr_len = new_len;
  ....
}

Предупреждение PVS-Studio: V527 It is odd that the '\0' value is assigned to 'char' type pointer. Probably meant: *body[new_len] = '\0'. http_request.c 370

Software weaknesses type — CWE-787: Out-of-bounds Write

Функция принимает указатель на указатель. Это позволяет при необходимости перевыделить память и вернуть адрес новой строки.

Ошибка кроется в строке:
body[new_len] = '\0';

Получается, что указатель на указатель интерпретируется как массив указателей. Никакого массива, конечно, нет. Поэтому NULL ('\0' в данном случае воспринимается как нулевой указатель) будет записан совершенно непонятно куда. Происходит порча какого-то неизвестного блока памяти.

Ох..


Помимо этого, возникает ещё одна ошибка. Строка не будет завершена терминальным нулём. В общем, всё плохо.

Правильный код:
(*body)[new_len] = '\0';

Условие всегда true/false (9 ошибок)


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

Первый вариант.
unsigned m_candiPageFirst;

bool
CIMIClassicView::onKeyEvent(const CKeyEvent& key)
{
  ....
  if (m_candiPageFirst > 0) {
    m_candiPageFirst -= m_candiWindowSize;
    if (m_candiPageFirst < 0) m_candiPageFirst = 0;
    changeMasks |= CANDIDATE_MASK;
  }
  ....
}

Предупреждение PVS-Studio: V547 Expression 'm_candiPageFirst < 0' is always false. Unsigned type value is never < 0. imi_view_classic.cpp 201

Software weaknesses type — CWE-570: Expression is Always False

Переменная m_candiPageFirst имеет тип unsigned. Следовательно, значение этой переменной не может быть меньше нуля. Чтобы защититься от переполнения, следует переписать код так:
if (m_candiPageFirst > 0) {
  if (m_candiPageFirst > m_candiWindowSize)
    m_candiPageFirst -= m_candiWindowSize;
  else 
    m_candiPageFirst = 0;
  changeMasks |= CANDIDATE_MASK;
}

Второй вариант.
void
QuickAccess::_grid_mostVisited_del(void *data, Evas_Object *)
{
  BROWSER_LOGD("[%s:%d]", __PRETTY_FUNCTION__, __LINE__);
  if (data) {
    auto itemData = static_cast(data);
    if (itemData)
      delete itemData;
  }
}

Предупреждение PVS-Studio: V547 Expression 'itemData' is always true. QuickAccess.cpp 571

Software weaknesses type — CWE-571: Expression is Always True

Это очень подозрительный код. Если указатель data != nullptr, то и указатель itemData != nullptr. Следовательно, вторая проверка не имеет смысла. Здесь мы столкнулись с одной из двух ситуаций:
  1. Это ошибка. На самом деле вместо оператора static_cast следовало использовать оператор dynamic_cast, который может вернуть nullptr.
  2. Настоящей ошибки нет, это неаккуратный код. Вторая проверка просто лишняя и её следует удалить, чтобы она не сбивала с толку анализаторы кода и других программистов.

Сейчас мне сложно сказать, следует выбрать пункт 1 или 2, но в любом случае, этот код заслуживает внимания и его следует исправить.

Третий вариант.
typedef enum {
  BT_HID_MOUSE_BUTTON_NONE = 0x00,
  BT_HID_MOUSE_BUTTON_LEFT = 0x01,
  BT_HID_MOUSE_BUTTON_RIGHT = 0x02,
  BT_HID_MOUSE_BUTTON_MIDDLE = 0x04
} bt_hid_mouse_button_e;

int bt_hid_device_send_mouse_event(const char *remote_address,
    const bt_hid_mouse_data_s *mouse_data)
{
  ....
  if (mouse_data->buttons != BT_HID_MOUSE_BUTTON_LEFT ||
      mouse_data->buttons != BT_HID_MOUSE_BUTTON_RIGHT ||
      mouse_data->buttons != BT_HID_MOUSE_BUTTON_MIDDLE) {
    return BT_ERROR_INVALID_PARAMETER;
  }
  ....
}

Предупреждение PVS-Studio: V547 Expression is always true. Probably the '&&' operator should be used here. bluetooth-hid.c 229

Software weaknesses type — CWE-571: Expression is Always True

Чтобы было проще понять в чем ошибка, я подставлю значения констант и сокращу код:
if (buttons != 1 ||
    buttons != 2 ||
    buttons != 4) {

Какое бы значение не хранилось в переменной, оно всегда будет не равно 1 или 2 или 4.

Другие ошибки:
  • V547 Expression 'ad->transfer_info' is always true. bt-share-ui-popup.c 56
  • V547 Expression 'item_name' is always true. SettingsUI.cpp 222
  • V547 Expression 'item_name' is always true. SettingsUI.cpp 226
  • V547 Expression '!urlPair' is always false. GenlistManager.cpp 143
  • V547 Expression 'strlen(s_formatted) < 128' is always true. clock.c 503
  • V547 Expression 'doc != ((void *) 0)' is always true. setting-common-data-slp-setting.c 1450

Путаница с enum (18 ошибок)


В презентации этот вид ошибки я пропустил, так как примеры слишком длинные. Но в статье, я думаю, есть смысл вспомнить про них.

Есть два enum, в которых объявлены константы с похожими именами:
typedef enum {
 WIFI_MANAGER_RSSI_LEVEL_0 = 0,
 WIFI_MANAGER_RSSI_LEVEL_1 = 1,
 WIFI_MANAGER_RSSI_LEVEL_2 = 2,
 WIFI_MANAGER_RSSI_LEVEL_3 = 3,
 WIFI_MANAGER_RSSI_LEVEL_4 = 4,
} wifi_manager_rssi_level_e;

typedef enum {
 WIFI_RSSI_LEVEL_0 = 0,
 WIFI_RSSI_LEVEL_1 = 1,
 WIFI_RSSI_LEVEL_2 = 2,
 WIFI_RSSI_LEVEL_3 = 3,
 WIFI_RSSI_LEVEL_4 = 4,
} wifi_rssi_level_e;

Неудивительно, что в именах можно запутаться и написать вот такой код:
static int
_rssi_level_to_strength(wifi_manager_rssi_level_e level)
{
  switch (level) {
    case WIFI_RSSI_LEVEL_0:
    case WIFI_RSSI_LEVEL_1:
      return LEVEL_WIFI_01;
    case WIFI_RSSI_LEVEL_2:
      return LEVEL_WIFI_02;
    case WIFI_RSSI_LEVEL_3:
      return LEVEL_WIFI_03;
    case WIFI_RSSI_LEVEL_4:
      return LEVEL_WIFI_04;
    default:
      return WIFI_RSSI_LEVEL_0;
  }
}

Переменная level имеет тип wifi_manager_rssi_level_e. Константы же имеют тип wifi_rssi_level_e. Получается, что есть сразу 5 неправильных сравнений, и поэтому анализатор выдаёт 5 предупреждений:
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. wifi.c 163
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. wifi.c 164
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. wifi.c 166
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. wifi.c 168
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. wifi.c 170

Software weaknesses type — CWE-697: Insufficient Comparison

Что забавно — этот код работает именно так, как задумывал программист. Благодаря везению, константа WIFI_MANAGER_RSSI_LEVEL_0 равна WIFI_RSSI_LEVEL_0 и так далее.

Несмотря на то, что сейчас код работает, это ошибка и её следует исправить. На это две причины:
  1. Этот код удивляет анализатор, а значит он будет удивлять и программистов, которые будут его сопровождать.
  2. Если хотя бы один из enum со временем изменится и значения констант перестанут совпадать, то программа начнет вести себя некорректно.

Другие неправильные сравнения:
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 885
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 889
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 892
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 895
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 898
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 901
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 904
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. e_devicemgr_video.c 907
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. myplace-placelist.c 239
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. myplace-placelist.c 253
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. myplace-placelist.c 264
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. telephony_syspopup_noti.c 82
  • V556 The values of different enum types are compared: switch(ENUM_TYPE_A) { case ENUM_TYPE_B:… }. telephony_syspopup_noti.c 87

Часть условия всегда true/false (2 ошибки)


Я заметил всего 2 таких ошибки, но они обе интересные, поэтому давайте рассмотрим их.
int bytestream2nalunit(FILE * fd, unsigned char *nal)
{
  unsigned char val, zero_count, i;
  ....
  val = buffer[0];
  while (!val) {                                            // <=
    if ((zero_count == 2 || zero_count == 3) && val == 1)   // <=
      break;
    zero_count++;
    result = fread(buffer, 1, read_size, fd);

    if (result != read_size)
      break;
    val = buffer[0];
  }
  ....
}

Предупреждение PVS-Studio: V560 A part of conditional expression is always false: val == 1. player_es_push_test.c 284

Software weaknesses type — CWE-570: Expression is Always False

Цикл выполняется до тех пор, пока переменная val равна нулю. В начале тела цикла переменная val сравнивается со значением 1. Естественно, переменная val не может быть равна 1, иначе бы цикл уже остановился. Здесь явно какая-то логическая ошибка.

Теперь рассмотрим другую ошибку.
const int GT_SEARCH_NO_LONGER = 0,
          GT_SEARCH_INCLUDE_LONGER = 1,
          GT_SEARCH_ONLY_LONGER = 2;

bool GenericTableContent::search (const String &key,
                                  int search_type) const
{
  ....
  else if (nkeys.size () > 1 && GT_SEARCH_ONLY_LONGER) {
  ....
}

Предупреждение PVS-Studio: V560 A part of conditional expression is always true: GT_SEARCH_ONLY_LONGER. scim_generic_table.cpp 1884

Software weaknesses type — CWE-571: Expression is Always True

Константа GT_SEARCH_ONLY_LONGER является частью условия. Это очень странно и у меня есть подозрение, что на самом деле условие должно выглядеть так:
if (nkeys.size () > 1 && search_type == GT_SEARCH_ONLY_LONGER)

Путаница с типами создаваемых и уничтожаемых объектов (4 ошибки)


Объявлены три структуры, никак не связанные между собой:
struct sockaddr_un
{
  sa_family_t sun_family;
  char sun_path[108];
};

struct sockaddr_in
{
  sa_family_t sin_family;
  in_port_t sin_port;
  struct in_addr sin_addr;
  unsigned char sin_zero[sizeof (struct sockaddr) -
    (sizeof (unsigned short int)) -
    sizeof (in_port_t) -
    sizeof (struct in_addr)];
};

struct sockaddr
{
  sa_family_t sa_family;
  char sa_data[14];
};

Ошибка заключается в том, что создаются объекты одного типа, а уничтожаются они как объекты другого типа:
class SocketAddress::SocketAddressImpl
{
  struct sockaddr *m_data;
  ....
  SocketAddressImpl (const SocketAddressImpl &other)
  {
    ....
    case SCIM_SOCKET_LOCAL:
        m_data = (struct sockaddr*) new struct sockaddr_un; // <=
        len = sizeof (sockaddr_un);
        break;
    case SCIM_SOCKET_INET:
        m_data = (struct sockaddr*) new struct sockaddr_in; // <=
        len = sizeof (sockaddr_in);
        break;
    ....
  }

  ~SocketAddressImpl () {
    if (m_data) delete m_data;                              // <=
  }
};

Предупреждения анализатора:
  • V572 It is odd that the object which was created using 'new' operator is immediately cast to another type. scim_socket.cpp 136
  • V572 It is odd that the object which was created using 'new' operator is immediately cast to another type. scim_socket.cpp 140

Software weaknesses type — CWE-762: Mismatched Memory Management Routines.

Создаются структуры типа sockaddr_un и sockaddr_in. При этом хранятся и уничтожаются они как структуры типа sockaddr. Типы всех трёх названных структур никак не связаны между собой. Это три разные структуры, имеющие разный размер. Сейчас код вполне может работать, так как структуры являются POD типами (не содержат деструкторы и т.д.) и вызов оператора delete превращается в простой вызов функции free. Однако формально код неверен. Нужно уничтожать объект точно такого же типа, который использовался при создании объекта.

Как я уже сказал, сейчас программа может работать, хотя формально и является неправильной. Надо понимать, что рассмотренный код очень опасен и достаточно, чтобы в одном из классов появился конструктор/деструктор или был добавлен член сложного типа (например std::string), как всё сразу сломается окончательно.

Другие ошибки:
  • V572 It is odd that the object which was created using 'new' operator is immediately cast to another type. scim_socket.cpp 167
  • V572 It is odd that the object which was created using 'new' operator is immediately cast to another type. scim_socket.cpp 171

Неправильная работа с printf-подобными функциями (4 ошибки)


static int _write_file(const char *file_name, void *data,
                       unsigned long long data_size)
{
  FILE *fp = NULL;

  if (!file_name || !data || data_size <= 0) {
    fprintf(stderr,
            "\tinvalid data %s %p size:%lld\n",
            file_name, data, data_size);
    return FALSE;
  }
  ....
}

Предупреждение PVS-Studio: V576 Incorrect format. Consider checking the third actual argument of the 'fprintf' function. Under certain conditions the pointer can be null. image_util_decode_encode_testsuite.c 124

Software weaknesses type — CWE-476: NULL Pointer Dereference

Возможна ситуация, когда указатель file_name будет содержать NULL. Как в таком случае поведёт себя функция printf, предсказать невозможно. На практике поведение будет зависеть от используемой реализации функции printf. См. дискуссию "What is the behavior of printing NULL with printf's %s specifier?".

Рассмотрим ещё одну ошибку.
void subscribe_to_event()
{
  ....
  int error = ....;
  ....
  PRINT_E("Failed to destroy engine configuration for event trigger.",
          error);
  ....
}

Предупреждение PVS-Studio: V576 Incorrect format. A different number of actual arguments is expected while calling 'printf' function. Expected: 1. Present: 2. surveillance_test_suite.c 393

Software weaknesses type — не знаю как классифицировать, буду благодарен за подсказку.

Макрос PRINT_E раскрывается в printf. Как видите, переменная error никак не используется. Видимо номер ошибки забыли распечатать.

Другие ошибки:
  • V576 Incorrect format. A different number of actual arguments is expected while calling 'printf' function. Expected: 1. Present: 2. surveillance_test_suite.c 410
  • V576 Incorrect format. A different number of actual arguments is expected while calling 'printf' function. Expected: 1. Present: 2. surveillance_test_suite.c 417

Проверка указателя выполняется уже после его разыменования (5 ошибок)


static void _show(void *data)
{
  SETTING_TRACE_BEGIN;
  struct _priv *priv = (struct _priv *)data;
  Eina_List *children = elm_box_children_get(priv->box);    // <=
  Evas_Object *first = eina_list_data_get(children);
  Evas_Object *selected =
    eina_list_nth(children, priv->item_selected_on_show);   // <=

  if (!priv) {                                              // <=
    _ERR("Invalid parameter.");
    return;
  }
  ....
}

Предупреждение PVS-Studio: V595 The 'priv' pointer was utilized before it was verified against nullptr. Check lines: 110, 114. view_generic_popup.c 110

Software weaknesses type — CWE-476: NULL Pointer Dereference

Указатель priv дважды разыменовывается в выражениях:
  • priv->box
  • priv->item_selected_on_show

И только затем указатель проверяется на равенство нулю. Чтобы исправить код, проверку следует передвинуть выше:
static void _show(void *data)
{
  SETTING_TRACE_BEGIN;
  struct _priv *priv = (struct _priv *)data;
  if (!priv) {
    _ERR("Invalid parameter.");
    return;
  }
  Eina_List *children = elm_box_children_get(priv->box);
  Evas_Object *first = eina_list_data_get(children);
  Evas_Object *selected =
    eina_list_nth(children, priv->item_selected_on_show);
  ....
}

Теперь рассмотрим более сложный случай.

Есть функция _ticker_window_create, в которой разыменовывается указатель, переданный функции в качестве аргумента.
static Evas_Object *_ticker_window_create(struct appdata *ad)
{
  ....
  // нет проверки указателя 'ad'
  ....
  evas_object_resize(win, ad->win.w, indicator_height_get());
  ....
}

Важно отметить, что указатель разыменовывается без предварительной проверки на равенство NULL. Другими словами, в функцию _ticker_window_create можно передавать только ненулевые указатели. Теперь посмотрим, как эта функция используется на самом деле.
static int _ticker_view_create(void)
{
  if (!ticker.win)
    ticker.win = _ticker_window_create(ticker.ad);       // <=
  if (!ticker.layout)
    ticker.layout = _ticker_layout_create(ticker.win);
  if (!ticker.scroller)
    ticker.scroller = _ticker_scroller_create(ticker.layout);

  evas_object_show(ticker.layout);
  evas_object_show(ticker.scroller);
  evas_object_show(ticker.win);

  if (ticker.ad)                                         // <=
    util_signal_emit_by_win(&ticker.ad->win,
      "message.show.noeffect", "indicator.prog");
  ....
}

Предупреждение PVS-Studio: V595 The 'ticker.ad' pointer was utilized before it was verified against nullptr. Check lines: 590, 600. ticker.c 590

Software weaknesses type — CWE-476: NULL Pointer Dereference

Указатель ticker.ad передаётся в функцию _ticker_window_create. Ниже есть проверка "if (ticket.ad)", которая свидетельствует о том, что этот указатель может быть нулевым.

Другие ошибки:
  • V595 The 'core' pointer was utilized before it was verified against nullptr. Check lines: 2252, 2254. media_codec_port_gst.c 2252
  • V595 The 'eyeCondition' pointer was utilized before it was verified against nullptr. Check lines: 162, 168. FaceEyeCondition.cpp 162
  • V595 The 'dev->name' pointer was utilized before it was verified against nullptr. Check lines: 122, 127. e_devicemgr_device.c 122

Не затираются приватные данные (1 ошибка)


static void SHA1Final(unsigned char digest[20],
                      SHA1_CTX* context)
{
  u32 i;
  unsigned char finalcount[8];
  ....
  memset(context->count, 0, 8);
  memset(finalcount, 0, 8);
}

Предупреждение PVS-Studio: V597 The compiler could delete the 'memset' function call, which is used to flush 'finalcount' buffer. The memset_s() function should be used to erase the private data. wifi_generate_pin.c 185

Software weaknesses type — CWE-14: Compiler Removal of Code to Clear Buffers

Компилятор вправе удалить функцию memset, которая затирает приватные данные в буфере finalcount. С точки зрения языка C и C++, вызов функции можно удалить, так как далее этот буфер нигде не используется. Хочу обратить внимание, что это не теоретически возможное поведение компилятора, а самое что ни на есть обыденное. Компиляторы действительно удаляют такие функции (см. V597, CWE-14).

Путаница с выделением и освобождением памяти (2 ошибки)


Первая ошибка.
void
GenericTableContent::set_max_key_length (size_t max_key_length)
{
  ....
  std::vector *offsets;
  std::vector *offsets_attrs;

  offsets = new(std::nothrow)                       // <=
            std::vector  [max_key_length];
  if (!offsets) return;

  offsets_attrs = new(std::nothrow)
                  std::vector  [max_key_length];
  if (!offsets_attrs) {
    delete offsets;                                 // <=
    return;
  }
  ....
}

Предупреждение PVS-Studio: V611 The memory was allocated using 'new T[]' operator but was released using the 'delete' operator. Consider inspecting this code. It's probably better to use 'delete [] offsets;'. scim_generic_table.cpp 998

Software weaknesses type — CWE-762: Mismatched Memory Management Routines

В переменной offset хранится указатель на массив объектов, созданных с помощью оператора new[]. Следовательно, разрушаться эти объекты должны с помощью оператора delete[].

Вторая ошибка.
static void __draw_remove_list(SettingRingtoneData *ad)
{
  char *full_path = NULL;
  ....
  full_path = (char *)alloca(PATH_MAX);                  // <=
  ....
  if (!select_all_item) {
    SETTING_TRACE_ERROR("select_all_item is NULL");
    free(full_path);                                     // <=
    return;
  }
  ....  
}

Предупреждение PVS-Studio: V611 The memory was allocated using 'alloca' function but was released using the 'free' function. Consider inspecting operation logics behind the 'full_path' variable. setting-ringtone-remove.c 88

Software weaknesses type — CWE-762: Mismatched Memory Management Routines

Память для буфера выделяется на стеке. Далее возможна ситуация, когда адрес этого буфера будет передан в качестве фактического аргумента в функцию free, что недопустимо.

Потенциально неинициализированная переменная (1 ошибка)


Тело функции _app_create, в которой находится ошибка, весьма длинное, поэтому я выделю только самую суть:
Eext_Circle_Surface *surface;
....
if (_WEARABLE)
  surface = eext_circle_surface_conformant_add(conform);
....
app_data->circle_surface = surface;

Преду

О мобильной библиотеке и любви к React Native

Среда, 12 Июля 2017 г. 11:07 + в цитатник
В апреле состоялась конференция для разработчиков мобильных приложений Mobius. На мероприятии Программу «Единая фронтальная система» представляли лидеры команды мобильной разработки Дмитрий Евстратов и Даниил Калинцев (Отдел развития платформы ЕФС АО СберТех) с рассказом о масштабируемом VIP архитектурном дизайне для компонентов с использованием React Native.
Для тех, кому интересна мобильная разработка Программы ЕФС, кто не присутствовал или не успел пообщаться с ребятами на конференции, мы подготовили короткое интервью. В комментариях к нему вы можете задать вопрос спикерам напрямую.
 

 
— Расскажите о мобильной разработке в Программе, над чем вы работаете?

— Отметим, что мобильная разработка в Программе появилась относительно недавно, около года. Преимущественно мы занимаемся созданием приложений для внутреннего клиента, т.е. сотрудников банка. В настоящий момент наша команда поддерживает одно мобильное приложение, разработанное полностью на нативных технологиях, но в ближайшем будущем мы планируем полностью перейти от создания нативных приложений с нуля к быстрой разработке приложений на Мобильной Платформе ЕФС, использующей технологию React Native. React Native позволяет нам решить задачу с обеспечением омниканального вывода продуктов банка в mobile и desktop клиенты, т.к. прикладной код теперь использует один и тот же технологический стек – TypeScript (мы за типизированный JavaScript), React и Redux. Мобильная платформа – это комбинация из набора back-end сервисов и front-end библиотеки компонентов для построения мобильных интерфейсов и удобной связи с back-end сервисами Мобильной Платформы. На Мобильной платформе уже разрабатывается ряд мобильных пользовательских сервисов и около десятка крупнофункциональных блоков, которые возможно переиспользовать для других функциональностей.
 
— Интересно, расскажите подробнее про мобильную библиотеку.

— Мобильная библиотека ЕФС – это два крупных направления: визуальные компоненты (кнопки, переключатели, стилизованные таблицы, выпадающие меню и т.д.) и невизуальные сервисы (логирование, аутентификация, удалённое конфигурирование работа с сетью и т.д.)
В задачи нашей команды входит весь жизненный цикл продукта: проектирование, разработка, тестирование, поддержка, а также планирование стратегического развития для поддержки динамически меняющегося бизнеса.
Мы стараемся подходить к разработке библиотеки с максимальной строгостью и щепетильностью. Стараемся довести компоненты и сервисы до совершенства. В процессе разработки у нас, конечно же, появляются трудности и проблемы, указывающие на несовершенство выбранной архитектуры. Но мы не отчаиваемся и итерационно её улучшаем.

Библиотека дала нам возможность плотно познакомиться с миром JavaScript и TypeScript в частности. Мы открываем для себя новые подходы, архитектурные паттерны (такие, как Flux) и нам это нравится, потому что расширяет наш кругозор и дает нам идеи для новых уникальных решений.

Стоит отметить, что в настоящий момент мы планируем использовать мобильную библиотеку только для внутренних проектов. Две ближайшие задачи помимо разработки и расширения возможностей компонентов – это разработка мобильных рабочих мест сотрудников (МРМ), динамически обновляющих свой контент с использованием возможности интерпретации JS налету, и переход к унифицированной с desktop версией библиотеки, позволяющей разрабатывать кросс-платформенные фронтальные сценарии. Эти задачи помогает нам решить React Native.

— Сколько времени может уйти на разработку компонента библиотеки?

— На компоненты уходит от недели до месяца. Простые можно сделать за неделю, не считая времени на тестирование. Для сложных компонентов потребуется около месяца активной разработки, чтобы обеспечить стабильность работы компонента и его полное покрытие тестами.
Внутри мы не перестаем дискутировать о стабильности технологии: сейчас React Native сам по себе 0.46, и финальная версия 1.0 еще будет не скоро, отметим, что и выпуск новых версий существенно замедлился.

С другой стороны, в каждой новой версии становится меньше исправлений. Сейчас версия стала выходить раз в месяц и по release notes можно сделать вывод, что количество исправлений не увеличилось в сравнении с 2-х недельными итерациями выпуска. Мы склоняемся к тому, что Facebook не откажется от технологии React Native, — уж слишком много сил ушло на него и многие продукты Facebook крутятся именно на нем.

Хотя риски есть, но мы не сильно переживаем, так как используем самую малую часть React Native, для нас это низкоуровневый движок, который является мостом между JS и Objective-C, а также строителем интерфейса приложения по DOM дереву, сгенерированному React JS. 80% функциональности наших компонентов никак не контактирует с особенностями React Native, поэтому если вдруг от него откажется Facebook, мы сможем сами написать подобную штуку. Опыт и понимание технологии у людей уже есть.

— В мобильной разработке вы цените «доступность низкоуровневых возможностей ПО», а как это сочетается с работой с React Native? Со стороны кажется, что она неизбежно должна быть менее низкоуровневой, чем традиционная мобильная разработка.

— Наверное, это сочетание в том, как мы это используем. Все низкоуровневые вещи мы пишем на Objective-C, на языке разработки мобильных приложений. Мы используем все нативные возможности платформы iOS, нам ничто не мешает использовать все что угодно, например, любые дополнительные фреймворки, которые используются только в IOS сообществе. React Native нисколько нас не ограничивает и позволяет использовать наш опыт мобильной разработки в полную силу. Мы экспериментируем и пробуем новые технологии в процессе создания библиотеки.
Что интересного можно отметить в технологическом стеке в общем: использование фреймворков Kiwi, Realm и Typhoon, Generamba для создания шаблонов наших модулей. React Native с Typhoon — это уникальная в своем роде штука. В целом, многое, что мы делаем в Программе ЕФС, уникально, аналогов на рынке нет по причине специфики задач и масштабов.

Нас часто спрашивают, когда стоит использовать React Native и зачем это нужно. Конкретно в нашем случае, React Native нужен для того, чтобы унифицировать стек разработки прикладных разработчиков, создать возможность для кросс-платформенных решений, а также динамических мобильных рабочих мест. React Native позволяет людям, не имеющим опыта в Objective-C, Swift, Java, Kotlin, создавать мобильные приложения на том же технологическом стеке, что и web-приложения.

— У части разработчиков существует предубеждение не против JavaScript в целом, а против его выхода за пределы фронтенда (вроде Node.js) — особенно. React Native тоже является таким выходом за пределы фронтенда?

— Можно сказать, что и так. React Native позволяет работать и с базами данных, и с внутренними механизмами самого устройства, например, геолокацией. Он позволяет использовать возможности мобильного устройства по максимуму.

— Большинство новых разработчиков сейчас учат сразу Swift. Что вы думаете о Swift? И что лучше изучать Objective-C или Swift?

— Идеально знать оба языка невозможно, но для новых разработчиков, нужно знать хотя бы в малой мере Objective-C и хорошо Swift: Objective-C все равно в какой-то мере нужен разработчикам, некоторые фишки лучше понять именно на нем. А так Swift – очень приятный язык, который сейчас стабилизируется. У него были сложности, когда они выпускали новые версии. У всех была головная боль, связанная с переходом на новую версию, т.к. очень часто менялся именно синтаксис. Но сейчас он стал только лучше. Есть уже много библиотек и статей по Swift. Objective-C за последний год стал сильно уходить на задний план.

— Какие результаты достигнуты?

— Завершен первый виток развития с React Native, пока нет массового запуска мобильных приложений, использующих нашу библиотеку. Сейчас у нас идет уже следующий виток, когда мы знаем многие фишки, которые предоставляет JavaScript и React Native. Теперь мы опираемся на наш опыт и идем дальше: есть динамическая подгрузка частей приложения, команда прикладных разработчиков будет делать не отдельные приложения, а сами бизнес-процессы.
Полноценного фидбэка на нашу библиотеку пока еще нет, только от одной команды разработчиков, им все нравится, они достаточно быстро делают свои приложения, используя библиотеку. Когда делаешь мобильное приложение на нативном коде, самое больное – когда надо сделать сложный UI, это всегда отнимает много времени. Они этим не занимаются и просто конструируют процесс. У них получается такое прототипирование, но более продвинутое. В целом, они довольны, а если они довольны, то и мы рады, а когда действительно пойдет прирост в скорости и будет реально ощутимая польза, будет настоящий прорыв.

В команде мобильной разработки Программы ЕФС сейчас 11 человек — 9 разработчиков, product owner и scrum master. В команде всего две девушки, одна из которых разработчик. Они очень толковые!

Перспектива есть, работы много. Это не даёт нам времени скучать и подталкивает к постоянному совершенствованию!
 
В ближайшее время мы планируем провести митап по темам мобильной разработки.
Поделитесь, пожалуйста, какие темы вам интересно было услышать и обсудить на митапе. Варианты ниже могут показаться общими, пожалуйста, конкретизируйте свои предложения в комментариях.
Чему быть на митапе?

Проголосовало 11 человек. Воздержалось 10 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332884/


Метки:  

Немного о багах в BIOS/UEFI ноутбуков Lenovo/Fujitsu/Toshiba/HP/Dell

Среда, 12 Июля 2017 г. 10:53 + в цитатник
В этой статье я приведу описание багов в BIOS/UEFI ноутбуков, с которыми приходилось работать и для которых приходилось адаптировать загрузчики. В первую очередь речь пойдет о багах, которые не видны пользователю, но которые могут помешать работе загрузчика даже при условии, что все было сделано правильно. Баги были выявлены как в интерфейсах соответствующих сред исполнения, так и в коде режима SMM процессоров Intel. Приводимый материал основывается на накопленном опыте, который растянут на достаточно большой период времени. Поэтому к моменту написания список конкретных моделей был утрачен. Тем не менее, сохранился список фирм-производителей, на ноутбуках которых возникали проблемы. Баги будут описаны последовательно, начиная с простых и заканчивая самыми сложными. Также по ходу описания будет приведен способ их обхода.


Прежде, чем мы начнем


Для того чтобы было полное понимание, при каких обстоятельствах приходилось сталкиваться с описываемыми проблемами, я кратко расскажу, какого сорта работу приходится выполнять. Существует продукт, шифрующий системный диск. Поэтому на этапе запуска ПК требуется расшифровывать диски, чтобы ОС могла запуститься. Поэтому был разработан загрузчик, который и выполняет эту роль. После установки всех своих перехватчиков этот загрузчик передает управление оригинальному загрузчику ОС. Далее в процессе описания термин “загрузчик” будет использоваться для обозначения нашего загрузчика. А термин “загрузчик ОС” будет использоваться для обозначения загрузчика, который мы подменяем.

Проблемы запуска загрузчика (Lenovo, UEFI)


Известно, что UEFI реализует глобальные переменные. В том числе существуют глобальные переменные, каждая из которых описывает вариант запуска ПК (load option entry). Также существует глобальная переменная BootOrder, которая описывает порядок вызова этих вариантов. Таким образом, загрузчик записывался на системный раздел UEFI, и для него создавалась новая запись, когда в BootOrder этот загрузчик ставился первым в очереди. Однако при запуске ПК вызывался загрузчик Windows. Выяснилось, что UEFI начисто игнорировал значение BootOrder и загружал всегда Windows, если находил его запись.

Проблему удалось обойти посредством подмены загрузчика самой Windows. Это, конечно, добавляет работы, т.к. теперь подменный файл надо защищать в самой операционной системе.

Проблемы при посылке USB-команд (HP, UEFI)


Загрузчик работает с USB-устройствами. А именно с CCID-ридерами. Для работы с USB-устройствами использовался предусмотренный для этих целей протокол — EFI_USB_IO_PROTOCOL. Проблема заключалась в том, что запущенный загрузчик не определял ни одного USB-устройства, когда на других ПК этот же загрузчик определял их. На первый взгляд могло показаться, что это полностью нефункционирующие драйвера USB, но при работе с ноутбуком я не мог обойти стороной тот факт, что ноутбук успешно запускался с флэшки. Далее выяснилось, что проблема возникает при посылке команд через управляющий канал (control transfer pipe) при помощи функции UsbControlTransfer протокола EFI_USB_IO_PROTOCOL. Прототип функции изображен ниже.

typedef EFI_STATUS (EFIAPI *EFI_USB_IO_CONTROL_TRANSFER) (
	IN EFI_USB_IO_PROTOCOL*		This,
	IN EFI_USB_DEVICE_REQUEST*		Request,
	IN EFI_USB_DATA_DIRECTION		Direction,
	IN UINT32				Timeout,
	IN OUT VOID*				Data OPTIONAL,
	IN UINTN				DataLength OPTIONAL,
	OUT UINT32*				Status
); 

Функция всегда возвращалась с ошибкой EFI_USB_ERR_TIMEOUT. Оказалось, что тип EFI_USB_DATA_DIRECTION был реализован разработчиками не в соответствии с UEFI спецификацией. Определение самого типа из спецификации приведено ниже.

typedef enum {
	EfiUsbDataIn,
	EfiUsbDataOut,
	EfiUsbNoData
} EFI_USB_DATA_DIRECTION; 

Ошибка в реализации типа заключалась в том, что на соответствующем ноутбуке EfiUsbDataIn и EfiUsbDataOut были перепутаны местами. Следовательно, когда загрузчик вызывал функцию UsbControlTransfer с третьим параметром равным EfiUsbDataOut, то в действительности происходила не запись в устройство, а чтение с него. И наоборот. Поскольку EfiUsbDataOut в коде приложения встречается первым, то получалось, что драйвер USB пытался прочитать данные с устройства, которых при посылаемых запросах быть не может. Соответственно, функция завершалась по таймауту.

Решение проблемы крайне некрасивое. При запуске загрузчик проверял, содержит ли поле FirmwareRevision структуры EFI_SYSTEM_TABLE строку «HPQ», и если содержит, проверялось, чтобы поле FirmwareRevision содержало значение 0x10000001. Если оба условия соблюдались, тогда при вызове соответствующих функций мы намерено меняли значения EfiUsbDataIn и EfiUsbDataOut на противоположные.

Проблемы при получении USB-ответов (Fujitsu LifeBook E743, UEFI)


Внешне проблема проявлялась в том, что не все CCID-устройства работали в загрузчике. Старые семейства работали безотказно, новые нет. Выяснилось, что проблема возникает при вызове функции UsbBulkTransfer протокола EFI_USB_IO_PROTOCOL. Функция всегда возвращала ошибку EFI_DEVICE_ERROR.

Известно, что USB хост контроллер обменивается данными с устройствами пакетами фиксированной длины. Также разработчиками USB допускается, что устройство может вернуть короткий пакет. В таком случае хост контроллер вернет состояние завершения передачи не “Success” а “ Short Packet”. И драйвером USB этот ответ интерпретировался как ошибка. Т.е. функция UsbBulkTransfer всегда возвращала EFI_DEVICE_ERROR в случае если устройство ответило коротким пакетом.

Так и получилось, что старые семейства CCID всегда отвечали длинными пакетами, когда новые – короткими. Проблему удалось обойти посредством анализа выходного буфера. Ниже на рисунке представлен формат RDR_to_PC_DataBlock пакетов CCID-устройств. Этот пакет устройство возвращает на такие команды, как PC_to_RDR_IccPowerOn, PC_to_RDR_Secure и PC_to_RDR_XfrBlock.

#pragma pack( push, 1 )

struct RDR_to_PC_DataBlock {

	UINT8		bMessageType;
	UINT32		dwLength;
	UINT8		bSlot;
	UINT8		bSeq;
	UINT8		bStatus;
	UINT8		bError;
	UINT8		bChainParameter;
	UINT8*		abData[0];

};

#pragma pack( pop ) 

Поле bMessageType идентифицирует тип пакета, и для RDR_to_PC_DataBlock пакета оно всегда равно 0x80. Поэтому перед получением ответа от устройства в буфере это поле предварительно обнулялось. Если функция UsbBulkTransfer возвращала ошибку, тогда проверялось значение этого поля, и если оно было равно 0x80, тогда считалось, что устройство на самом деле ответило корректно. В таком случае поле dwLength использовалось для вычисления размера ответа, и этот размер уже возвращался изначальному запросчику.

Проблемы при работе с картой памяти (Toshiba Satellite U200, BIOS)


Внешне проблема проявлялась в том, что загрузчик отказывался работать, т.к. не мог найти участок памяти, в котором он бы мог разместиться. Анализ выявил проблемы во время сканирования карты памяти. В процессе этого сканирования часть диапазонов пропускалась и не подвергалась анализу.

Речь идет о сервисе 0xe820 прерывания int 15h. Т.к. загрузчик оставлял часть кода резидентно, требовалось выделять память и размещать его код в этом участке. Со своей стороны это требовало модификации карты памяти, чтобы операционная система во время своего запуска не использовала выделенный нами участок. Соответственно, во время запуска вся карта считывалась, должным образом модифицировалась и подменялась через перехват int 15h.

Ниже приведены входные и выходные параметры функции получения карты памяти.

  • Входные параметры:
    • EAX – код функции, всегда равен 0xe820;
    • EBX – продолжение, при первом вызове значение должно быть равно 0, при последующих значение должно быть равно значению, возвращенное функцией после вызова. Данный регистр указывает функции, с какой записи продолжить получение карты памяти;
    • ES:DI – указатель буфера, куда возвращается запись, описывающая конкретный диапазон памяти;
    • ECX – размер буфера, должно быть не меньше 20, т.к. первые ревизии этой функции возвращали записи размером 20 байт. На современных системах размер записи составляет 24 байта;
    • EDX – сигнатура, всегда равно 'SMAP'. Используется для верификации запросчика.

  • Выходные параметры:
    • CF – ошибка, если 0, значит ошибки нет;
    • EAX — сигнатура, всегда равно 'SMAP'. Используется для верификации BIOS;
    • ES:DI – указатель буфера, то же, что и на входе;
    • ECX – размер записи, который вернула функция;
    • EBX – значение, которое следует подать на вход функции для получения следующей записи. Также не следует делать предположения о самом значении, т.к. это может быть смещение, индекс или любая другая сущность во внутреннем представлении самой функции.

Посредством этой функции загрузчик в цикле вычитывает всю карту памяти. И загрузчик был разработан таким образом, чтобы обеспечить прямую совместимость с будущими версиями BIOS. Т.е. на входе регистр ECX содержал 64. Как следует из описания самой функции, функция в регистре ECX вернет размер записи, которая была записана в буфер. Поскольку на текущий момент максимальный размер записи 24, то больше, чем это значение, в регистре оказаться не могло. Также функция всегда должна возвращать ровно одну запись.

Однако на конкретном ноутбуке оказалось, что функция интерпретирует значение ECX несколько иначе. Т.е. она используется не для того чтобы определить размер записи, которую поддерживает запросчик, а для того чтобы определить, сколько функция может вернуть вообще записей за один вызов. Так и получилось, что при вызове функции загрузчик считывал не по одной записи, а по две. И, следовательно, одна из них всегда игнорировалась загрузчиком. Это и привело к тому, что загрузчик не смог найти участок памяти, в котором он мог бы разместить резидентный код.

Проблема была решена посредством передачи в ECX значения 24. Т.е. от идеи прямой совместимости пришлось отказаться. Были мысли о том, как определить размер записи, но, понимая стабильность разных версий BIOS, есть риск, что алгоритм из-за этого также не будет стабильно работать.

Проблемы при остановке USB 3.0 и переинициализации PIC контроллеров (HP, BIOS)


Визуально проблема выглядела так: после того, как пользователь успешно подключил смарт-карту и ввел ПИН, экран темнел, выводилось сообщение о том, что идет загрузка ОС, и все останавливалось именно на этом сообщении. ПК зависал намертво.

Поскольку загрузчик BIOS разработан на базе RTOS, сама пользовательская оболочка работает в защищенном режиме процессора, что, безусловно, требовало переинициализации классического PIC контроллера. Соответственно, при передаче управления загрузчику ОС процессор возвращался в реальный режим. И это в свою очередь требовало возврат PIC контроллера в исходное состояние.

Предварительный анализ выявил, что процессор возвращался в реальный режим, но далее происходило повисание ПК. Далее выяснилось, что проблема возникала только в том случае, если загрузчик инициализировал USB хост контроллеры. Перед возвратом в реальный режим и перед возвратом PIC контроллера в исходное состояние также останавливались USB хост контроллеры.

USB 3.0 хост контроллер может иметь регистр USBLEGSUP. Данный регистр позволяет передавать управление контроллером от BIOS к ОС и наоборот. В первую очередь, это может понадобиться, например, для эмуляции классических клавиатурных портов ввода/вывода, дабы обеспечить совместимость со старым ПО. Т.е. при обращении к этим портам произойдет SMI прерывание, а все остальное уже сделает обработчик этого прерывания. А на современных машинах все чаще и чаще используются только USB-клавиатуры. Формат регистра описан ниже.

  • Capability ID (Биты 0-7) – идентификатор функциональности. Для данного регистра поле равно 1
  • Next Capability Pointer (Биты 8-15) – указатель на следующий capability регистр
  • HC BIOS Owned Semaphore (Бит 16) – если установлен, значит BIOS управляет хост контроллером
  • Зарезервировано (Биты 17-23)
  • HC OS Owned Semaphore (Бит 24) – перед использованием хост контроллера операционная система должна установить этот бит, в ответ на это BIOS сбросит бит 16, после чего можно использовать хост контроллер
  • Зарезервировано (Биты 25-31)

RTOS при остановке хост контроллера также сбрасывает бит 24 регистра USBLEGSUP. Таким образом, она возвращает управление над ним к BIOS. Далее RTOS выполняет возврат PIC контроллера в исходное состояние. Также известно, что PIC контроллер аппаратно уже не существует, и он также эмулируется посредствам SMM-режима. Следовательно, когда выполнялся возврат PIC контроллера в исходное состояние, при работе с его регистрами происходило SMI прерывание. Анализ выявил, что поскольку RTOS не дожидается установки бита 16 в регистре USBLEGSUP и поскольку сразу после установки бита 24 этого регистра выполнялся возврат PIC контроллера в исходное состояние, код режима SMM возвращал управление над хост контроллером, а PIC контроллер, который, по сути, породил SMI прерывание, не обрабатывался вообще. Поскольку инициализация PIC выполняется в несколько этапов, контроллер остался частично в непроинициализированном состоянии. Из-за этого ломалась доставка прерываний. Сразу после возврата процессора в реальный режим при первом прерывании процессор вставал на невалидный вектор, из-за чего он начинал выполнять бессмысленный поток инструкций.

Проблему удалось обойти посредством ожидания установки бита 16 в регистре USBLEGSUP перед возвратом PIC в исходное состояние.

Проблемы доставки прерываний от PIC контроллера (Dell Latitude E7240, BIOS)


Внешне проблема выглядела так: когда загрузчик запустился и вывел приглашение к подключению смарт-карты, загрузчик зависал намертво. При этом проблема возникала исключительно при перезагрузке ПК, при включении все работало нормально.

Предварительный анализ выявил, что процессор сваливался в page fault. Последующее изучение проблемы показало, что RTOS для каждого прерывания использует отдельные стеки, размер которых очень маленький (256 байт). Все эти стеки располагаются смежно, как это отражено на рисунке ниже.


Также удалось выяснить, что page fault происходил на странице памяти, которая следовала сразу перед страницей со стеками прерываний. Поэтому последующий анализ выполнялся уже на этом уровне.

RTOS при инициализации USB хост контроллера также включает доставку прерываний PIC с линии, на которой располагается контроллер. Обработчик прерывания при вызове разрешает все прерывания на процессоре, после чего последовательно вызывает зарегистрированные обработчики для этой линии. После вызова всех зарегистрированных обработчиков обработчик прерывания посылает команду завершения прерывания (EOI) PIC контроллеру.

Известно, что PIC контроллер имеет ISR регистр. Этот регистр используется для того, чтобы определить, какие прерывания в настоящий момент обрабатывает процессор, а какие нет. И если процессор обрабатывает конкретное прерывание, то даже если на соответствующей линии присутствует запрос, оно доставлено не будет. До тех пор, пока процессор не выдаст EOI команду PIC контроллеру, после чего PIC возобновит доставку этого прерывания.

Последующий анализ выявил, что в процессе вызова зарегистрированных обработчиков PIC контроллер доставлял прерывание повторно, даже несмотря на то, что команда EOI еще не была послана PIC. Безусловно, это ошибка эмуляции PIC контроллера. Это приводило к тому, что сначала переполнялся стек соответствующего прерывания, затем портился стек других прерываний, и, в конечном счете, доступ выполнялся к неотображенной странице памяти. И это приводило к page fault, обработчик которого останавливает работу загрузчика.

Проблему удалось обойти посредством запрета доставки соответствующего прерывания на PIC контроллере до вызова зарегистрированных обработчиков и его разрешением после их вызова.

Заключение


Приведенный список багов является далеко не полным. Описаны только те случаи, которые удалось вспомнить. Хуже всего то, что радикального решения проблемы стабильности придумать до сих пор не удалось. Удалось только добиться стабильности лишь в отдельных моментах. Все равно попадаются экземпляры с ошибками, которые опытному разработчику придется выдумывать. И еще хуже, тратить по три дня на анализ и устранение проблемы. А некоторые случаи бывают далеко не легкими. Три дня на устранение проблемы это, конечно, не так много, но когда проблем с десяток, это уже хорошо выбивает из рабочего графика.

Понимание реальности вынудило на реверс-инжиниринг загрузчика Windows с целью понять, какие механизмы он использует. Для меня это означает, что я также могу ими пользоваться безопасно. Если отойти от этих правил, тогда работа загрузчика гарантированной быть не может.

После еще пары проблем с USB в UEFI я пришел к тому, чтобы в загрузчик поместить свои драйвера хост контроллеров. Для этого приходится останавливать те драйвера, что работают в самом UEFI, и загружать свои. Добавлять так называемые “костыли” мне никогда не импонировало. К тому же, такой код со временем станет тяжело развивать из-за нагроможденности.

А что касается своих драйверов, в этом есть большой смысл, т.к. есть FastBoot режим, который не гарантирует загрузку USB-драйверов. Это не баг, но камень в сторону самого UEFI как стандарта, который не предоставляет механизма догрузки незагруженных драйверов.

В завершение описания проблем хотелось бы отметить следующее: складывается впечатление, что нынешние BIOS/UEFI разрабатываются в отрыве от полного понимания принципов работы этих систем, либо тестирование не проводится должным образом. По опыту, и то и другое имеет место. Достаточно того, чтобы на произведенном ПК запускался Windows и Linux. Все остальное — издержки производства. А кого будет обвинять клиент, думаю, рассказывать не надо.

Исходя из опыта работы, BIOS и UEFI являются самыми нестабильными средами исполнения. В особенности, EFI MacBook является особым исключением, и работать с ним тяжелее всего. Но это уже другая история.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332908/


Метки:  

Что нового в CUBA Platform 6.5

Среда, 12 Июля 2017 г. 10:52 + в цитатник
Вы могли заметить, что мы опубликовали новые минорные релизы платформы CUBA и CUBA Studio. В новой версии реализованы улучшения, в основном касающиеся текущего функционала, но мы добавили и несколько новых полезных фич.

Под катом:

  • Uber JAR
  • UI-компонент для предиктивного поиска
  • Версионирование REST API
  • Балансировка нагрузки в кластере
  • Компонент приложения ZooKeeper
  • Улучшения в Polymer UI
  • Поддержка Bean Validation в CUBA Studio
  • Поддержка Groovy в слушателях сущностей



Uber JAR


Наиболее ожидаемая фича в сообществе разработчиков платформы. Благодаря Uber JAR мы максимально упростили распространение CUBA приложений.
Для создания Uber JAR достаточно определить нужную конфигурацию в CUBA Studio или в вашей IDE и в пару кликов собрать JAR файлы.

CUBA Studio overview

Чтобы запустить приложение, достаточно выполнить две команды:
java -jar app-core.jar
java -jar app.jar

Uber JAR уже содержит в себе все зависимые компоненты, а также легкий HTTP Сервер. Дополнительную информацию можно найти в этой главе документации.

Ввод текста с подсказками


В наборе визуальных компонентов появился SuggestionPickerField. По мере ввода текста пользователем, он выполняет поиск в фоновом режиме и предлагает подходящие варианты значений.

SuggestionPickerField

Подробнее – в документации. «Вживую» компонент можно пощупать в онлайн демо-приложении Sampler.

Версионирование REST API


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

Для решения этой проблемы, в новой версии CUBA добавлен механизм трансформации REST запросов. Правила трансформации могут быть заданы в простом XML формате, согласно которому платформа приводит запросы в старом формате к последней версии API.

Например, если мы переименуем сущность OldOrder в NewOrder, а также её атрибут date в deliveryDate, то используя нижеприведённый XML для трансформаций, мы сохраним совместимость с клиентами, которые используют старый API.



    
        
        
            
        
    


Как обычно, детали описаны в документации.

Балансировка нагрузки в кластере


До версии 6.5 в платформе не поддерживалась балансировка нагрузки между веб- и средним слоем. Соответственно, веб сервер с самого начала выбирал сервер среднего слоя из списка cuba.connectionUrlList и не менял его до того момента, пока тот не становился недоступным.
В последней версии платформы распределение осуществляется на уровне пользовательской сессии. Сервер среднего слоя выбирается случайным образом во время первого удаленного соединения. Установленная сессия является sticky и закрепляет выбранный сервер на всей своей продолжительности. Если запрос приходит от анонимной сессии или без сессии, то закрепления не происходит и запрос идет на случайно заданный сервер в кластере.
Если в отдельном случае Вам потребуется самостоятельно задать правила распределения, то это можно будет сделать переопределением бина cuba_ServerSorter, который по умолчанию использует класс RandomServerSorter.

Компонент приложения ZooKeeper


Благодаря добавлению этого компонента значительно упрощается управление конфигурацией кластера. Новый компонент интегрирует Ваше приложение с Apache ZooKeeper – централизованным сервисом для поддержания информации о конфигурации.
Теперь для управления топологией кластера Вам достаточно дать адрес ZooKeeper. После этого серверы среднего слоя будут публиковать свои адреса через каталог ZooKeeper, а механизмы обнаружения нового компонента будут запрашивать у ZooKeeper адреса доступных серверов. Если сервер среднего слоя становится недоступным, то он автоматически исключается из каталога — в тот же момент или после таймаута.
Исходный код компонента приложения с документацией доступен на GitHub. Бинарные артефакты опубликованы в стандартных репозиториях CUBA.

Улучшения в Polymer UI


Судя по активности на форуме поддержки, всё больше разработчиков начинает использовать новый веб-клиент для CUBA приложений на основе Google Polymer. В этом релизе мы добавили ряд фич, которые призваны упростить разработку.
Во-первых, при открытии на редактирование сущности из списка, пользователь будет перенаправлен на форму для редактирования, находящуюся по другому URL. То есть теперь можно использовать стандартный механизм отслеживания истории в браузере, и прямые ссылки на редактор конкретной сущности.
Вторая доработка – это компонент cuba-lookup. Он позволяет выбирать значение ссылочного атрибута не из выпадающего списка, а из всплывающего окна со списком сущностей.
Наконец, в библиотеке визуальных компонентов добавлен компонент для загрузки файлов на сервер. Загруженный файл отображается в виде активной ссылки.

Поддержка Bean Validation в CUBA Studio


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

Bean Validation

Поддержка Groovy в слушателях сущностей


Теперь CUBA Studio поддерживает скаффолдинг обработчиков событий изменения сущностей (Entity Listeners) на Groovy. Для этого достаточно включить Groovy Support в Project properties > Advanced tab.

Groovy Entity Listeners

Резюме


В этой статье описаны наиболее важные изменения Платформы и Студии. С полным списком изменений можно ознакомиться здесь:

platform-6.5-release (англ.)
studio-6.5-release (англ.)

Также, приглашаем посмотреть видеозапись вебинара, посвящённой релизу (англ.).
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332906/


Метки:  

Проблемы безопасности Android-приложений: классификация и анализ

Среда, 12 Июля 2017 г. 10:09 + в цитатник


Изображение: etnyk, CC BY-NC-ND 2.0

Использование смартфонов в повседневной жизни не ограничивается голосовыми звонками и СМС. Возможность загружать и выполнять программы, а также мобильный доступ в Интернет привели к появлению громадного числа мобильных приложений. Функциональность современного смартфона составляют браузеры, клиентские программы социальных сетей, офисные приложения и всевозможные сервисы, работающие в Сети. Android-устройства заняли б'oльшую часть рынка смартфонов за счет открытой архитектуры платформы Android и удобного API разработчика. На данный момент Android является наиболее популярной мобильной ОС с долей рынка более 75%. Количество приложений, загруженных из магазина Google Play, в 2016 году составило 65 миллиардов [1].

Параллельно наблюдается и бурный рост числа вредоносных приложений: в 2015 году их было обнаружено 2,3 миллиона [3]. Кроме того, около 60% Android-устройств работают на версиях ОС с известными уязвимостями, и они потенциально могут быть атакованы злоумышленниками [6]. Эти угрозы, в свою очередь, привели к развитию средств защиты. Официальный магазин Google Play ввел фильтрацию приложений с помощью песочницы Google Bouncer. Антивирусные компании стали выпускать свои продукты под Android (хотя, как показано в [8], многие из них сами по себе содержат опасные уязвимости). Число научных публикаций по данной теме также возросло: одна из обзорных работ 2015 года о решениях безопасности для Android [2] насчитывает более 40 проектов и упоминает далеко не все известные на данный момент исследования. В связи с тем, что мобильные устройства являются источником и хранилищем большого количества конфиденциальных данных, проблема безопасности ОС Android и ее приложений стоит особо остро.

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

1. Устройство ОС Android


В основе ОС Android лежит ядро Linux с некоторыми архитектурными изменениями, которые были сделаны инженерами Google. Приложения для операционной системы Android разрабатываются на языке Java. Начиная с версии Android 1.5 был представлен набор инструментов Android NDK, который позволяет разрабатывать модули приложений на языках С и С++ и компилировать их в машинный код [4]. Приложения поставляются в виде файлов специального формата APK, который является ZIP-архивом с определенной структурой каталогов и файлов. APK-файл приложения содержит:

  • манифест;
  • модули, скомпилированные в машинный код (разделяемые библиотеки .so);
  • различные ресурсы приложения;
  • DEX-файл;
  • прочие необходимые файлы.

Начиная с версии Android 4.4 поддерживаются две среды выполнения приложений: Dalvik VM и ART. Следует отметить, что процесс подготовки APK-файла не изменился, изменился только процесс установки приложений в новой среде выполнения ART. Начиная с версии 5.0 среда выполнения ART стала использоваться по умолчанию вместо Dalvik VM.

Среда выполнения Java-программ Dalvik VM на Android сильно отличается от «обычной» Java VM. Во-первых, при компиляции Java-программы она компилируется в байт-код виртуальной машины Dalvik, который сильно отличается от байт-кода виртуальной машины HotSpot. Виртуальная машина Dalvik является регистровой, что делает ее выполнение на RISC-архитектурах, часто используемых в мобильных устройствах, более эффективным. Также при разработке Dalvik учитывались ограничения памяти в мобильных устройствах. Начиная с версии Android 2.2 среда выполнения Dalvik содержит JIT-компилятор, который компилирует «горячие» куски кода Java-программ в машинный код [5]. Стандартные библиотеки Java были либо заменены на доработанные библиотеки из пакета Apache Harmony либо написаны заново. При компиляции Java-программы получается файл формата DEX (Dalvik Executable), который содержит байт-код для виртуальной машины Dalvik. Формат файла был разработан с целью сокращения объема занимаемой памяти и существенно отличается от стандартного формата JAR. Для вызова модулей, реализованных в машинном коде, из Java-программ используется интерфейс JNI. Стоит отметить, что имеется возможность подгружать машинные модули динамически по сети с помощью компонента DexClassLoader.

2. Проблемы безопасности


Родительским процессом для всех приложений в ОС Android является процесс Zygote. Данный процесс представляет собой каркас Android-приложения, в котором уже загружены все необходимые библиотеки окружения Android, но отсутствует код самого приложения. Запуск приложения Android с точки зрения операционной системы происходит следующим образом:

  1. Вначале происходит системный вызов fork для создания потомка от процесса Zygote (см. рис. 1).
  2. В этом новом процессе открывается файл запускаемого приложения (системный вызов open).
  3. Происходит чтение информации о файлах классов (classes.dex) и ресурсов из файла приложения. Происходит открытие сокетов для IPC.
  4. Выполняется системный вызов mmap для отображения файлов приложения в память.
  5. Среда выполнения производит настройку необходимого окружения и выполняет приложение (интерпретирует байт-код Dalvik или передает управление функциям в исполняемом коде в случае ART [7]).

На уровне ядра ОС Android каждое приложение является отдельным процессом с уникальными значениями user/group ID, которые даются ему при установке. Таким образом, каждая программа работает в своей песочнице. Начиная с версии 4.3 в дополнение к этой политике безопасности добавилось использование компонента мандатного контроля доступа SELinux [10].


Рис. 1. Запуск нового приложения в ОС Android

По умолчанию приложение ограничено в своих возможностях и не может сделать ничего, чтобы негативно повлиять на другое приложение или пользователя: ни прочитать пользовательские данные, ни модифицировать системные приложения; отсутствует доступ к сети. Для получения доступа к различным ресурсам приложение обращается к сервисам ОС Android. Разрешения на доступ задаются статически в файле манифеста приложения и выдаются приложению во время его работы по мере необходимости. Система запрашивает у пользователя согласие на выдачу этих разрешений во время установки или во время обновления приложения. Ответственность за выдачу доступа приложению лежит на пользователе, он самостоятельно решает, какому приложению давать разрешения на определенные действия, а какому не давать, — во время его установки. Использование такого подхода, при котором все разрешения выдаются разом при установке приложения, привело к тому, что пользователи стали раздавать полномочия приложениям, не задумываясь о последствиях. В свою очередь, приложения стали запрашивать все больше разрешений по мере расширения их функциональности. В Android 6 Marshmallow данная система заменена на другую: приложение запрашивает доступ к ресурсам у пользователя во время его работы, а пользователь может либо разрешить доступ, либо запретить его [11].

Android-приложение состоит из четырех видов компонентов и не содержит функции main() или какой-то другой единой точки входа. Компоненты приложений взаимодействуют друг с другом с помощью специальных сообщений, называемых Intent.

Компоненты под названием Activity определяют пользовательский интерфейс. Как правило, используется один компонент Activity для описания одного экрана приложения. Activity может запустить другое Activity, передав параметры с помощью механизма Intent. Во время работы только одно Activity может работать и обрабатывать пользовательский ввод, остальные на это время остаются замороженными или уничтожаются, в зависимости от выбранного режима работы приложения.

Компоненты под названием Service [9] производят фоновую обработку. Когда Activity нужно выполнить какую-то операцию, например загрузку файла или проигрывание музыки, и продолжить работу, когда пользователь перешел в другое приложение или свернул текущее, приложение запускает сервис, цель которого — выполнение этой операции. Разработчики могут использовать Service в качестве приложения-демона, стартующего во время загрузки системы. Компонент Service, как правило, поддерживает RPC (Remote Procedure Call), поэтому другие компоненты системы могут обращаться к нему.

Компонент Content provider сохраняет и обменивается данными, используя интерфейс реляционных баз данных. Каждый Content provider содержит уникальный URI для данных и обрабатывает запросы к нему в виде очередей SQL (Select, Insert, Delete).

Компоненты Broadcast receiver выступают в роли ящиков для сообщений от других приложений.

Проблемы безопасности, возникающие в ОС Android, рассматривались в работах [2, 12, 13], но классификация проблем по категориям дана только в статье [2]. Эта классификация достаточно подробная и охватывает многие проблемы безопасности, но у нее есть ряд существенных недостатков: некоторые категории охватывают широкую область уязвимостей, в то время как другие достаточно специализированы; приведенные категории уязвимостей никак не соотносятся с программными уровнями архитектуры ОС Android; не охвачены уязвимости из интернет-источников, а также слабо охвачены уязвимости в протоколах и аппаратуре (п. 2.7 и 2.8 в нашей классификации). Предлагаемая ниже классификация уязвимостей устраняет эти недочеты.

2.1. Уязвимости ядра Linux и его модулей


К данной категории проблем относятся уязвимости, которые присущи всем ОС, основанным на той же версии ядра Linux, что и ОС Android. Эксплойты, использующие уязвимости в ядре, могут получить данные пользователя или права администратора системы. Повысив привилегии процесса до прав администратора системы, вредоносная программа может отключить систему безопасности Android, вставить в существующие программы вредоносный код и установить руткит. К тому же производители различных устройств добавляют в ядро модули для своих устройств; в этих модулях также могут быть уязвимости. Примеры уязвимостей повышения привилегий описаны в [14, 15, 16, 64]. Также стоит отметить, что совсем недавно была обнаружена уязвимость в компоненте ashmem для межпроцессного взаимодействия в Android [62].

2.2. Уязвимости модификаций и компонентов производителей устройств


В последнее время производители различных мобильных устройств стали выпускать свои модифицированные прошивки Android. Эти прошивки могут содержать различные приложения и сервисы, разработанные производителем устройства, которые чаще всего нельзя удалить. Например, в октябре 2016 года был обнаружен скрытый бэкдор в прошивках Foxconn [63]. Анализ этих сервисов, приведенный в статьях [17], показывает, что в них содержится от 65 до 85% уязвимостей, обнаруженных во всей системе. К тому же стоит отметить, что уязвимости, которые были обнаружены и исправлены в основной ветке Android, могут долгое время оставаться в ветках производителей устройств [18, 19].

2.3. Уязвимости модулей в машинных кодах


Android-приложения поддерживают запуск машинного кода через интерфейс JNI. Это порождает еще одну категорию уязвимостей, связанную с широко известными уязвимостями утечек памяти в низкоуровневых языках (например, в С и С++ ) [20]. Поскольку на уровне процессов ОС Android нет никаких ограничений, кроме накладываемых ядром Linux, сторонние библиотеки в машинных кодах могут использовать разрешения, выданные всему приложению, для совершения вредоносной активности (см. также следующую категорию уязвимостей, п. 2.4). Также модули приложения в машинных кодах используются авторами вредоносных приложений, чтобы обойти инструменты анализа и мониторинга уровня Android. Эти модули также могут использовать техники противодействия анализу, используемые в обычных программах.

2.4. Уязвимости механизмов межкомпонентного взаимодействия


К данной категории относятся уязвимости, связанные с взаимодействием между различными компонентами приложений. Так как на уровне операционной системы приложение ограничено песочницей процесса, ему необходим механизм доступа к компонентам ОС Android для взаимодействия с ними. Это происходит через устройство /dev/Binder и различные другие сервисы ОС Android. Как уже говорилось выше, параметры этого доступа задаются в файле манифеста, в виде XML-файла с разрешениями. Анализ, приведенный в статьях [12, 21, 22, 23, 24, 25], указывает на недочеты этой системы ограничений и показывает пути их обхода. Так, например, приложение может воспользоваться правами доступа другого приложения и получить с помощью него данные через ICC. Также могут быть уязвимости, связанные со сторонними библиотеками. Сторонние библиотеки, используемые в приложении, получают тот же набор ограничений, что и само приложение. Поэтому сторонние библиотеки могут использовать разрешения, выданные всему приложению, для совершения вредоносной активности. Приложения к тому же могут перехватывать системные события, пересылаемые через широковещательный запрос, и сохранять информацию о входящих звонках и СМС.

2.5. Уязвимости в самих приложениях


Каждое приложение сохраняет какие-то данные о пользователе. Эти данные должны быть защищены должным образом, чтобы к ним не могли получить доступ другие приложения, — но такая защита предусмотрена не всегда. Например, Skype в одной из версий приложения сохранял базу данных контактов в открытом виде на диске. Таким образом, контакты можно было прочитать любым другим приложением, у которого есть доступ к диску [26]. Также приложения могут использовать криптографические библиотеки с ошибками [27] или же какие-то собственные реализации. К тому же не все приложения производят хорошую аутентификацию и авторизацию пользователя. Кроме этого, приложения могут позволять SQL-инъекции и подвержены атакам XSS. Также стоит отметить, что большинство разрабатываемых приложений написаны на Java без использования какой-либо защиты для бинарного кода, а байт-код Java, как известно, легко поддается дизассемблированию и анализу. Стоит отметить, что к этой категории уязвимостей относится также известный список Mobile OWASP-10. Многие подобные уязвимости описаны в [28, 29].

2.6. Уязвимости во встроенных сервисах и библиотеках


Стандартный набор библиотек и сервисов, работающих в Android, также содержит уязвимости. Например, недавно была обнаружена уязвимость Stagefright в библиотеке для отображения видео в MMS-сообщениях, которой были подвержены все версии Android, начиная с 2.2 [30]. Позже была обнаружена уязвимость в компоненте MediaServer, которой подвержены все версии Android c 2.3 до 5.1 [31]. В статье [13] показаны уязвимости рантайма Dalvik: запустив большое количество процессов в системе, можно добиться, что последующий процесс запустится с правами администратора.

2.7. Интернет-источники


Android-приложения распространяются через широкое количество источников помимо официального магазина приложений. Поскольку Android-приложения написаны в основном на Java, то они легко поддаются обратной разработке и переупаковке с использованием вредоносного кода [32, 33]. Кроме того, как было показано в статье [34], песочницу анализа приложений Bouncer, используемую в официальном каталоге, легко обойти. Поэтому и в самом официальном магазине содержится большое количество вредоносных программ. Кроме этого, Android поддерживает удаленную установку приложений через Google Play на устройства, связанные с Google-аккаунтом. Таким образом, если взломать учетную запись Google для устройства, можно установить из Google Play вредоносное приложение, которое туда предварительно загрузил злоумышленник. При этом на экране мобильного телефона не требуется каких-либо подтверждений этих действий, поскольку они запрашиваются в окне браузера и приложение устанавливается на телефон в фоновом режиме при получении доступа к Интернету. Также к этой категории уязвимостей относится использование социальной инженерии, когда для продолжения работы предлагают установить приложение из неавторизованного источника [35].

2.8. Уязвимости аппаратуры и связанных с ней модулей и протоколов


Мобильные устройства, работающие под управлением ОС Android, имеют широкий набор аппаратуры для взаимодействия с внешним миром. Соответствующие уязвимости можно эксплуатировать при непосредственной близости к устройству или при наличии физического доступа к устройству. Примерами таких атак служат атака типа «отказ в обслуживании» на технологию Wi-Fi Direct [36], кража данных кредитных карт с помощью NFC [37], исполнение удаленного кода через Bluetooth [38], установка вредоносного приложения без ведома пользователя через adb с помощью механизма бэкапов [39]. В работе [13] показаны уязвимости, с помощью которых можно повысить привилегии приложения, используя ошибки в реализации протокола adb.

3. Инструменты для анализа Android-приложений


С того момента, как были выпущены первые Android-телефоны, было написано большое количество инструментов для анализа Android-приложений. Наиболее полный обзор этих инструментов содержится в статьях [40, 41, 42, 2]. В первых трех источниках инструменты классифицированы по видам анализа: статический, динамический и их объединение (смешанный). В статье [2] используется классификация инструментов по стадиям развертывания приложения на Android-устройстве. В нашей работе используется классификация по видам анализа.

Статический анализ


Инструменты статического анализа можно разделить на следующие категории:

  • Инструменты, извлекающие метаинформацию из манифеста приложения и предоставляющие информацию о запрашиваемых разрешениях, компонентах Activity, сервисах и зарегистрированных Broadcast receivers. Метаинформация часто используется позже в динамическом анализе для запуска функций приложения.
  • Инструменты для модификации существующего байт-кода приложения с использованием техники инструментирования. Это позволяет, например, добавлять трассирование функциональности в существующие приложения.
  • Инструменты, которые реализуют декомпилятор или дизассемблер байт-кода Dalvik.

Одним из наиболее популярных инструментов обратной разработки является Apktool [43]. Он имеет возможности декодирования ресурсов приложения приблизительно в оригинальную форму, переупаковки приложений обратно в APK/JAR-файлы, отладку байт-кода smali. Для декомпилирования и компилирования в apktool байт-кода Dalvik используется другой широко известный проект smali/backsmali [44]. Также для дизассемблирования байт-кода Dalvik часто применяется инструмент Dedexer [45].

Radare2 [46] — инструмент с открытым исходным кодом для дизассемблирования, анализа, отладки и изменения бинарных файлов Android-приложения.
Один из самых разносторонних инструментов для статического анализа — Androguard [47]. Он может дизассемблировать и декомпилировать приложение обратно в исходный код Java. Получив два APK-файла, он может посчитать коэффициент их похожести для детектирования переупакованных приложений или известных вредоносных приложений. Благодаря своей гибкости он используется в некоторых инструментах смешанного анализа.

Более полный список инструментов статического анализа можно найти в статьях, указанных выше. Следует отметить, что статический анализ имеет ряд существенных ограничений, связанных с тем, что имеется лишь абстрактное представление о программе. Например, статический анализ становится намного сложнее, если к программе применены обфусцирующие преобразования. В зависимости от сложности обфускации некоторые (а может, и все) статические подходы могут стать абсолютно бесполезными. Если вызов каждого метода делается только косвенно, вряд ли удастся построить граф вызовов программы. В Android такое преобразование можно сделать, используя Java Reflection API для вызова методов и создания объектов вместо использования обычных вызовов и инструкций создания нового объекта. На рынке решений по защите исходного кода уже представлено несколько продуктов для обфускации файлов Android-приложений, которые могут свести на нет все преимущества статического анализа [48, 49]. Более подробно о техниках противодействия статическому анализу можно почитать в 50.

Динамические и смешанные инструменты анализа


Инструменты динамического анализа отслеживают поведение неизвестного приложения во время выполнения при запуске его в контролируемой песочнице для получения поведенческого следа. Для этого производится инструментирование песочницы на различных уровнях архитектуры участками кода, которые отслеживают поведение приложения и ОС Android.



Рис. 2. Уровни архитектуры песочницы Android

Архитектура песочницы Android представляет из себя эмулятор Android (чаще всего QEMU), внутри которого работает ОС Android. Инструментирование производится либо на уровне эмулятора, либо на уровне ОС Android, либо и там и там. Уровень ОС Android также делится на четыре подуровня:

  • приложения,
  • среда разработки приложения,
  • рабочее окружение приложения и библиотеки,
  • ядро и его модули.

Техники, используемые в динамическом анализе приложения:

  • Отслеживание помеченных данных. Такие инструменты используются, чтобы отслеживать потенциальные утечки конфиденциальной информации.
  • Мониторинг системных вызовов. Инструменты могут записывать системные вызовы с помощью инструментирования виртуальных машин, strace или модуля ядра. Это позволяет производить трассировку машинного кода.
  • Трассировка методов (функций). Инструменты могут отслеживать вызовы Java-методов приложения в виртуальной машине Dalvik, вызовы функций в машинных кодах через JNI, системные вызовы и прерывания.

К инструментам смешанного анализа относятся инструменты, которые сочетают в себе техники динамического и статического анализа.

4. Идеальная система полносистемного анализа платформы Android


Статьи [40, 41, 42, 2] насчитывают более 40 различных инструментов как для анализа Android-приложений, так и для анализа ОС Android в целом. Как было замечено в статьях [34, 60, 61], существующие инструменты динамического анализа обладают рядом серьезных недостатков. Данные недостатки присущи и стандартному инструменту анализа приложений в магазине Google Play — Google Bouncer, вследствие чего вредоносные приложения могут распространяться через официальный магазин, не будучи обнаруженными.

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



Архитектура системы представлена на рис. 3. Основным компонентом является полносистемный эмулятор, который может загружать как прошивки ОС Android с реальных устройств, так и официальный образ Android для эмулятора. Эмулятор поддерживает проброс датчиков от реального устройства, а также исполнение отдельных участков кода на реальных устройствах. Внутри эмулятора работают модули, которые обеспечивают:

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

Список требований к данной системе:

1. Поддержка загрузки прошивок от производителей различных устройств


Существенным недостатком всех существующих инструментов анализа ОС Android и ее приложений является невозможность запуска прошивок от производителей устройств в доступных эмуляторах. Как было показано в п. 2.2, модифицированные прошивки от производителей устройств содержат от 65 до 85% уязвимостей, обнаруженных во всей системе. На данный момент не существует инструментов для анализа, которые позволяли бы запускать прошивки от производителей. Все существующие решения работают на специальной сборке Android для виртуальной платформы GoldFish.

2. Возможность исполнения отдельных кусков машинного кода на реальном устройстве


По информации из статей [34, 61], существуют способы выявления работы внутри эмулятора. Как правило, выявляется выполнение в эмуляторе QEMU в режиме бинарной трансляции, поскольку именно на нем основано большинство песочниц. Способы основаны на разном поведении определенных участков машинного кода в эмуляторе и на реальном устройстве. Так как изменение механизма бинарной трансляции представляется сложной задачей, исполнение отдельных кусков машинного кода на реальном устройстве было бы хорошим подходом для противостояния данным способам обнаружения эмулятора.

3. Проброс данных от датчиков оборудования реальных устройств в эмулятор


Как было описано в статьях [34, 61], существуют способы определения эмулятора, основанные на данных, получаемых от датчиков оборудования, таких как акселерометр, гироскоп, GPS, датчик света, силы тяжести. Выходные значения этих датчиков основаны на информации, собранной из окружающей среды, и следовательно, реалистичная их эмуляция является сложной задачей. Присутствие такого рода датчиков — основное различие между смартфонами и настольными компьютерами. Увеличивающееся число датчиков на смартфонах дает новые возможности для идентификации мобильных устройств.

4. Совместный анализ на уровне Java-кода и машинного кода


Затруднением для многих систем анализа Android-приложений является то, что приложения содержат как модули в байт-коде Dalvik, так и модули в машинных кодах. Из существующих решений поддержка анализа всех модулей реализована только в DroidScope [57] и CopperDroid [58, 59]. Идеальная система анализа должна позволять отслеживать потоки данных и управления на уровне пользовательского и системного кода. Для пользовательского кода, когда это возможно, следует поднимать уровень представления до Java-кода, являющегося высокоуровневым языком разработки. Также необходимо обеспечивать «склейку» потоков данных и управления при переходе от машинного кода к Java и наоборот.

5. Поддержка анализа межкомпонентного взаимодействия


В статье о CopperDroid [58] показана реализация поддержки анализа межкомпонентного взаимодействия как внутри Android-приложения, так и между различными приложениями. Это сделано с помощью перехвата данных, проходящих через компонент ядра Binder, так как все взаимодействие проходит через него. Подход, реализованный в CopperDroid, позволяет не производить модификации исходного кода Android, что делает возможность его переноса на новые версии ОС Android и новую среду запуска приложений ART с минимальными изменениями.

6. Защита от статических эвристик обнаружения


Как показано в статьях [[57], 61], большинство песочниц для анализа можно обнаружить, просто проверив значения IMEI, IMSI или номера сборки прошивки у устройства. Также эмулятор может быть обнаружен, если проверить на стандартные значения таблицу маршрутизации. Из существующих решений защита от обнаружения по статическим эвристикам реализована только в проекте ApkAnalyzer [65].

7. Минимальные изменения прошивок Android


Также стоит отметить, что многие решения динамического анализа основаны на инструментировании кода различных компонентов ОС Android, в частности виртуальной машины Dalvik. Это осложняет их дальнейшую поддержку, а также миграцию в новую среду запуска приложений ART. Многие песочницы ограничиваются только анализом кода компонентов Java, тогда как все больше и больше приложений используют компоненты в машинных кодах.

8. Поддержка полносистемного анализа помеченных данных с отслеживанием потоков управления


Стоит отметить, что многие из инструментов динамического анализа используют анализ помеченных данных, используя для этого инструмент TaintDroid [56]. В статье [60] были показаны успешные способы обхода анализа данного инструмента. Причиной успешности данных атак являются следующие факты: 1) TaintDroid отслеживает только потоки данных и не отслеживает потоки управления, 2) TaintDroid отслеживает потоки данных только на уровне виртуальной машины Dalvik и проходящих через интерфейс JNI. Возможные пути разрешения данных недостатков описаны в [60] и дают направление для дальнейших исследований.

9. Поддержка символьного исполнения и частичного исполнения с конкретными значениями с использованием данных, полученных из статического анализа кода (concolic execution — смешанное исполнение)


В статье [51] описана среда анализа, реализующая данный подход. Эта среда сочетает в себе техники статического анализа графа потока управления программы, символьного исполнения программы и исполнения программы с конкретными значениями. Подходы, сочетающие техники символьного исполнения и исполнения программы с конкретными значениями, описаны в статьях [52, 53, 54, 55]. Целью данного метода является наблюдение путей исполнения, которые приводят к секциям программы, содержащим «интересный» код. Под «интересным» кодом понимают такой код, выполнение которого зависит от каких-либо произошедших внешних событий или настроек окружения системы. Например, код классов в Android, динамически подгружаемый с помощью компонента DexClassLoader или вызов «машинных» методов через интерфейс JNI.

Заключение


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

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

Несмотря на такую пессимистичную картину, в последнее время наблюдается ряд позитивных движений в сторону улучшения безопасности Android. В частности, компания Google запустила программу выпуска обновлений безопасности: они выходят каждый месяц и некоторые вендоры все же добавляют их в свои версии прошивки (устройства, поддерживаемые Google, получают эти обновления первыми). Кроме того, пользователи могут самостоятельно установить на свои устройства версию ОС Android CyanogenMod (ныне LineageOS), которая поддерживает эти обновления безопасности. Также пользователь может снизить риски, если ограничится набором популярных приложений только из официального магазина Google Play. Уязвимости типа RCE (удаленное выполнение кода) встречаются все реже, поэтому доставка вредоносных приложений на телефоны чаще происходит через фишинговые сайты, неофициальные магазины приложений или с помощью социальной инженерии. Среднестатистический пользователь ОС Android, соблюдая определенную технику безопасности, может быть спокоен за свой смартфон.

Автор: Михаил Романеев (@melon)


Ссылки и использованные материалы:


  1. statista.com/statistics/281106/number-of-android-app-downloads-from-google-play
  2. Tan D. J. J. et al. Securing Android: A Survey, Taxonomy, and Challenges // ACM Computing Surveys (CSUR). 2015. Vol. 47. № 4. P. 58.
  3. file.gdatasoftware.com/web/en/documents/whitepaper/G_DATA_Mobile_Malware_Report_H1_2016_EN.pdf
  4. developer.android.com/ndk/guides/stable_apis.html
  5. Dalvik VM Internals // sites.google.com/site/io/dalvik-vm-internals
  6. securityweek.com/overwhelming-majority-android-devices-dont-have-latest-security-patches
  7. Google I/O 2014 — The ART runtime // youtube.com/watch?v=EBlTzQsUoOw
  8. media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEFCON-24-Huber-Rasthofer-Smartphone-Antivirus-And-Security-Applications-Under-Fire.pdf
  9. developer.android.com/guide/components/services.html
  10. source.android.com/devices/tech/security/selinux
  11. developer.android.com/preview/features/runtime-permissions.html
  12. Enck W., Ongtang M., McDaniel P. Understanding android security // IEEE security & privacy. 2009. № 1. P. 50—57.
  13. Shabtai A., Mimran D., Elovici Y. Evaluation of Security Solutions for Android Systems // arXiv preprint arXiv:1502.04870. — 2015.
  14. Hei X., Du X., Lin S. Two vulnerabilities in Android OS kernel // Communications (ICC), 2013 IEEE International Conference on. IEEE, 2013. P. 6123—6127.
  15. forum.xda-developers.com/showthread.php?t=2048511
  16. Zhou X. et al. Identity, location, disease and more: Inferring your secrets from android public resources // Proceedings of the 2013 ACM SIGSAC conference on Computer & communications security. ACM, 2013. P. 1017—1028.
  17. Wu L. et al. The impact of vendor customizations on android security // Proceedings of the 2013 ACM SIGSAC conference on Computer & communications security. ACM, 2013. P. 623—634.
  18. en.wikipedia.org/wiki/Stagefright_(bug)
  19. Zhou X. et al. The peril of fragmentation: Security hazards in android device driver customizations // Security and Privacy (SP), 2014 IEEE Symposium on. IEEE, 2014. P. 409—423.
  20. Sun M., Tan G. NativeGuard: Protecting android applications from third-party native libraries // Proceedings of the 2014 ACM conference on Security and privacy in wireless & mobile networks. ACM, 2014. P. 165—176.
  21. Octeau D. et al. Effective inter-component communication mapping in android with epicc: An essential step towards holistic security analysis // USENIX Security 2013.
  22. Chin E. et al. Analyzing inter-application communication in Android // Proceedings of the 9th international conference on Mobile systems, applications, and services. ACM, 2011.
  23. Felt A. P. et al. Permission Re-Delegation: Attacks and Defenses // USENIX Security Symposium. 2011.
  24. Bugiel S. et al. Xmandroid: A new android evolution to mitigate privilege escalation attacks // Technische Universit"at Darmstadt, Technical Report TR-2011-04.
  25. Bugiel S. et al. Towards Taming Privilege-Escalation Attacks on Android // NDSS. 2012.
  26. cvedetails.com/cve/CVE-2011-1717
  27. Fahl S. et al. Why Eve and Mallory love Android: An analysis of Android SSL (in) security // Proceedings of the 2012 ACM conference on Computer and communications security. ACM, 2012. P. 50—61.
  28. owasp.org/index.php/Projects/OWASP_Mobile_Security_Project_-_Top_Ten_Mobile_Risks
  29. Lu L. et al. Chex: statically vetting android apps for component hijacking vulnerabilities //Proceedings of the 2012 ACM conference on Computer and communications security. ACM, 2012. P. 229—240.
  30. kb.cert.org/vuls/id/924951
  31. CVE-2015-3842 // cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-3842
  32. Zhou Y. et al. Hey, You, Get Off of My Market: Detecting Malicious Apps in Official and Alternative Android Markets // NDSS. 2012.
  33. Nolan G. Decompiling android. – Apress, 2012.
  34. Petsas T. et al. Rage against the virtual machine: hindering dynamic analysis of android malware // Proceedings of the Seventh European Workshop on System Security. ACM, 2014. P. 5.
  35. Android Security Underpinnings // youtube.com/watch?v=NS46492qyJ8
  36. coresecurity.com/advisories/android-wifi-direct-denial-service
  37. securityaffairs.co/wordpress/37667/hacking/nfc-attack-credit-card.html
  38. zerodayinitiative.com/advisories/ZDI-15-092/
  39. securityfocus.com/archive/1/535980/30/150/threaded
  40. Neuner S. et al. Enter sandbox: Android sandbox comparison // arXiv preprint arXiv:1410.7749. 2014.
  41. Hoffmann J. From Mobile to Security: Towards Secure Smartphones: дис. – 2014.
  42. Faruki P. et al. Android Security: A Survey of Issues, Malware Penetration and Defenses.
  43. ibotpeaches.github.io/Apktool
  44. github.com/JesusFreke/smali
  45. dedexer.sourceforge.net
  46. radare.org/r
  47. github.com/androguard/androguard
  48. dexprotector.com
  49. guardsquare.com/dexguard
  50. PANDORA applies non-deterministic obfuscation randomly to Android, Schulz P. Code protection in android // Insititute of Computer Science, Rheinische Friedrich-Wilhelms-Universit"at Bonn, Germany. 2012.
  51. Sch"utte J., Fedler R., Titze D. Condroid: Targeted dynamic analysis of android applications // in review. 2014.
  52. Sen K. DART: Directed Automated Random Testing // Haifa Verification Conference. 2009. Vol. 6405. P. 4.
  53. Sen K., Marinov D., Agha G. CUTE: a concolic unit testing engine for C. ACM, 2005. Vol. 30. № 5. P. 263—272.
  54. Godefroid P. Random testing for security: blackbox vs. whitebox fuzzing // Proceedings of the 2nd international workshop on Random testing: co-located with the 22nd IEEE/ACM International Conference on Automated Software Engineering (ASE 2007). ACM, 2007. P. 1.
  55. Jayaraman K. et al. jFuzz: A Concolic Whitebox Fuzzer for Java // NASA Formal Methods. 2009. P. 121—125.
  56. Enck W. et al. TaintDroid: an information-flow tracking system for realtime privacy monitoring in smartphones // ACM Transactions on Computer Systems (TOCS). 2014. Vol. 32. № 2. P. 5.
  57. Yan L. K., Yin H. DroidScope: Seamlessly Reconstructing the OS and Dalvik Semantic Views for Dynamic Android Malware Analysis // USENIX Security Symposium. 2012. P. 569—584.
  58. Tam K. et al. CopperDroid: Automatic Reconstruction of Android Malware Behaviors // Proc. of the Symposium on Network and Distributed System Security (NDSS). 2015.
  59. copperdroid.isg.rhul.ac.uk/copperdroid
  60. Sarwar G. et al. On the Effectiveness of Dynamic Taint Analysis for Protecting against Private Information Leaks on Android-based Devices // SECRYPT. 2013. P. 461—468.
  61. Jing Y. et al. Morpheus: automatically generating heuristics to detect Android emulators // Proceedings of the 30th Annual Computer Security Applications Conference. ACM, 2014. P. 216—225.
  62. googleprojectzero.blogspot.ru/2016/12/bitunmap-attacking-android-ashmem.html
  63. bbqand0days.com/Pork-Explosion-Unleashed
  64. powerofcommunity.net/poc2016/x82.pdf
  65. apk-analyzer.net
  66. www.phdays.ru/program/fast-track/45984
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332904/


Метки:  

«Наэкономили»: К чему ведет в магазине чрезмерное урезание бюджетов на ИТ

Среда, 12 Июля 2017 г. 09:53 + в цитатник
image

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

Кейс № 1. Дешевле — не значит лучше


С экономией на оборудовании в последние пару лет нам чаще всего пришлось сталкиваться в разрезе Единой государственной автоматизированной информационной системы (ЕГАИС) и 2D-сканеров. Существуют общепризнанные производители этого оборудования (например, Zebra Technologies, Datalogic), но некоторые наши заказчики при переходе на работу по требованиям ЕГАИС решали сэкономить и приобретали дешевые сканеры различных китайских ноунеймов. Как выяснилось, с поставленными задачами эти устройства справляются со скрипом: не всегда считывают акцизные марки быстро и корректно, а также практически неудобны в настройке, поскольку не имеют драйверов. В результате, магазинам приходилось сталкиваться с многочисленными проблемами в работе с УТМ — некорректное считывание марки и невозможность продажи товара или отправка некорректных сведений.

Резюме. Как говорится, скупой платит дважды: чтобы магазины все же отвечали требованиям ЕГАИС, нашим клиентам приходилось отказаться от экономии на 2D-сканерах и повторно их приобретать (однако уже нормальные устройства от надежных производителей).

Кейс № 2. Долго, дорого и больно


Про экономию на оборудовании вообще можно рассказывать бесконечные истории. Чаще всего она приводит к несовместимости устройств. Допустим, ритейлер решил скомпоновать POS-терминал из системного блока одного производителя, дисплеи покупателя — у второго. Главное, чтобы было дешевле. Формально, экономия получается не более 10-20 долларов с комплекта касс, однако приводит к тому, что ИТ-департаменту заказчика или нашим специалистам приходится тратить много времени, чтобы «подружить» разрозненное оборудование разных вендоров.

Например, один из клиентов «Пилота» — сеть продовольственных магазинов — решил закупить новое POS-оборудование, выбрав для этой цели какого-то недорого поставщика. У него системные блоки стоили дешево, однако он умолчал о совместимости партий железа: как выяснилось, в одной и той же поставке могло быть несколько системных блоков с разной компонентной базой. Этот выбор клиента аукнулся при инсталляции ПО. Обычно оно заливается централизованно с предустановленных образов. Но при инцидентах, возникающих с этими системными блоками и первичной заливкой, приходилось на каждой машине инсталлировать операционную систему и драйвера с нуля. В итоге при выезде на инсталляцию инженер на настройку каждой кассы тратил по 1,5–2 часа (а этого времени обычно хватает на то, чтобы установить софт на весь магазин).

Резюме. В случае нашего клиента 15% бюджета, сэкономленного при покупке касс, были потрачены на время инженеров, занимающихся инсталляцией ПО.

image

Кейс № 3 Домашняя версия вместо бизнес-варианта


Еще один популярный вид экономии — экономия на лицензиях системного ПО. Один из наших заказчиков в качестве серверов магазина использовал рабочую станцию на Windows XP. То есть с ее помощью производилось большое количество сетевых работ (управление кассами, товародвижением, прайс-чекерами, видеонаблюдением и весами). Хотя стоит помнить, что обычная домашняя версия Windows XP позволяет выдерживать не более 15 одновременных подключений. И если на магазине с двумя кассами и рабочей станцией администратора такая конструкция еще могла функционировать, то магазин с 4-5 кассами работал уже не совсем корректно: на кассы могли не выгружаться цены, не все чеки попадали в товародвижение. Такой «сервер» часто отказывался работать, особенно в часы высоких нагрузок.

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

Кейс № 4. Непонимание важности процесса обучения персонала


Один из клиентов «Пилота» — сеть продуктовых магазинов — открывая новую торговую точку, решил сэкономить (причем достаточно небольшие деньги) на обучении персонала. И научил работать с кассовой программой «Профи-Т» не всех кассиров, а только нескольких. ИТ-департамент понадеялся, что за оставшиеся до открытия дни прошедшие обучение сотрудники передадут компетенцию коллегам, но просчитались. В день Х выяснилось, что за кассами сидят люди, которые «Профи-Т» едва ли не впервые в глаза видят и уж точно не умеют с ней работать. И, кроме того, не понимают и изменившиеся в связи с внедрением нового ПО бизнес-процессы. Как следствие, ритейлеру пришлось столкнуться с огромным количеством негатива как со стороны покупателей, так и со стороны сотрудников. Да и по карману такая «экономия» ударила: в день открытия нового магазина было много возвратов, брошенных покупок. А это еще и репутационный удар.

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

Для сравнения, вот как протекают процессы в магазинах с обученным и необученным персоналом:
  • в магазине с обученным персоналом в день открытия происходит примерно 100 возвратов, в магазине с необученным персоналом — 150;
  • в магазине с обученным персоналом в день происходит порядка 15 отмен чеков, в магазине с необученным персоналом — 20;
  • в магазине с обученным персоналом в день может быть зафиксировано 100 отмен позиций в чеке, в магазине с необученным персоналом — 300;
  • скорость работы кассира по с чеку в магазине с обученным персоналом — 20 секунд, в магазине с необученным персоналом — 40 секунд;
  • количество чеков в магазине с обученным персоналом в день — 100, в магазине с необученным персоналом — 300.

image

Кейс № 5. Игнорирование рекомендаций — не выход


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

Резюме. Не удивительно, что менеджменту пришлось в срочном порядке устранять огрехи и учесть рекомендации со стороны ИТ-специалистов.

Кейс № 6. Своими силами — не всегда лучше


Есть ритейлеры, которые пытаются сэкономить на услугах подрядчика, и какие-то задачи решить собственными силами. Оно и понятно — не все могут сразу свыкнуться с мыслью, что, например, внедрение софта, монтаж и настройка оборудования может обойтись в сумму порядка миллиона рублей. Однако ИТ-инсорс не всегда обладает необходимой компетенцией по специфическому оборудованию, чтобы качественно и быстро выполнить работу. В результате все сводится к тому, что первоначальная экономия на услугах подрядчика приводит к тратам собственных ресурсов. Например, один клиент решил открыть пилотный магазин с нашей помощью, а остальные — своими силами. Однако на первом же полностью собственном открытии он понял, что, помимо существующих внутренних ИТ-задач, сотрудникам приходится еще тратить кучу времени на дополнительные. И время, которое на это уходило, оказалось в разы больше того, что тратили на открытие магазина наши специалисты. Для большей наглядности приведем статистику: на монтаж и настройку одной кассы наши специалисты, в среднем, тратят 1 час. У ИТ-сотрудников на стороне клиента, обычно, это занимает не менее 3 часов.

Резюме. Клиент, потеряв время, часть средств, в итоге все равно обратился за помощью к нам, чтобы не занимать внутренних сотрудников задачами по открытию магазинов.

Как вы понимаете, на этом истории про то, как магазины экономят на ИТ и что из этого получается не заканчиваются. Уверены, что и вам приходилось сталкиваться с подобными кейсами. Если да, делитесь в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331896/


Метки:  

Как поднять продажи в Москве на 60% в период кризиса? История успеха одной компании

Среда, 12 Июля 2017 г. 09:47 + в цитатник
Всем привет! В этой статье я расскажу о том, как производитель детского и спортивного оборудования в период с 2014 по 2017 год поднял продажи на 60% и увеличил долю рынка на 3,5% с помощью правильного подхода к интернет-маркетингу.

image


Немного о компании


Группа Компаний «Забава» более 10 лет функционирует на российском торгово-производственном рынке, работает в основном в Центральных регионах России. Собственное производство находится в Московской и Брянской областях.
Главной миссией «Забавы» является улучшение ситуации с организацией детского досуга – производство и поставка на рынок России обучающего и развивающего детского оборудования высокого качества с максимальным уровнем безопасности. Большое внимание уделяется устройству уличных спортивных площадок для активных видов спорта – волейбола, баскетбола, футбола.

Проблема – отсутствие роста продаж при расширении производства


«Мы начинали бизнес в Москве, затем вышли в регионы. К 2014 году построили более 40 площадок, 90% из них – в столице и области. Клиентов привлекали путем рекламирования продукции с помощью каталогов, буклетов и презентаций. 40% продаж приносил нам сайт и продвижение в Интернете. С учетом расширения производства возникла острая проблема нехватки новых клиентов, не было роста продаж. Мы понимали, что наш сайт и его продвижение нуждаются в доработке»
Алексей Красников, генеральный директор ГК «Забава»

Группе Компаний в 2014 году требовалось организовать качественное и эффективное привлечение клиентов из Интернета, так как масштабировать оффлайн-каналы в условиях жесткой конкуренции в Москве не представлялось возможным. Для решения свои задач ГК «Забава» обратилась в интернет-агентство «Веб-Центр», в котором я руковожу отделом маркетинга. В стратегии интернет-агентства по увеличению продаж клиента были 3 главных направления. Связка из этих трех направлений и должна была обеспечить стабильный рост оборота и прибыли Группе Компаний:

1. Полная переделка устаревшего сайта.
2. Качественное SEO-продвижение.
3. Внедрение процесса повышения конверсии.

Расскажу подробнее о каждом этапе, думаю, эти практические знания пригодятся многим читателям.

Этап 1. Переделываем сайт

«Сегодня компании делают полный редизайн сайта в среднем 1 раз в 4-5 лет, и этот срок сокращается каждый год. Это связано с развитием новых технологий и инструментов, возможностей CMS, изменением поведения пользователей Сети. К 2014 году сайт ГК «Забава» сильно устарел в технологическом плане – он был не адаптивным, CMS не позволяла серьезно улучшить SEO-продвижение. Высокая конкуренция на московском рынке усложняла удержание позиций компании в поисковых системах»
Роман Плотников, директор интернет-агентства «Веб-Центр»

В новом сайте zabava-sport.ru были реализованы:

  • Адаптивный дизайн.
  • Полноценный каталог с возможностью быстрой обратной связи.
  • Раздел с готовыми проектами спортивных площадок.
  • Информация о выполненных проектах с фото.
  • Контентные разделы сайта для качественного SEO-продвижения.


image

Этап 2. Делаем правильное SEO-продвижение

Были проведены анализ данных и подготовка сайта:
  • проверка возраста домена, внешних ссылок, наложенных санкций, анализ контента,
  • проверка конкурентов, сравнение семантических ядер,
  • распределение продвигаемых запросов и определение посадочных страниц,
  • техническая проверка для ускорения индексации,
  • формирование метаданных, «правильного» сниппета и т.д.

Далее на основе анализа было подготовлено техническое задание и план проработки посадочных страниц (контент-план). Затем уже началось внесение изменений в сайт в соответствие с техническим заданием:
  • прописывание уникальных метаданных на все страницы сайта, тегов Title, H1, атрибутов изображений, оптимизация заголовков,
  • настройка файлов «robots» и «sitemap»,
  • работы с Яндекс.Вебмастер,
  • написание текстов для посадочных страниц,
  • работа над коммерческими и поведенческими факторами,
  • оптимизация навигации сайта и т.д.

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

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

Этап 3. Повышаем конверсию

Повышение конверсии и продающих свойств сайта – циклический процесс, который состоит из следующих основных шагов:
  1. Анализ данных и ключевых показателей эффективности работы сайта (технический анализ, эвристический анализ, юзабилити-анализ, веб-аналитика).
  2. Выдвижение обоснованных гипотез для повышения конверсии и A/B-тестирований.
  3. Исправление ошибок, доработки и эксперименты по повышению конверсии.

Подробно я описал процесс повышения конверсии в этой статье на Хабре, она еще не потеряла актуальность, пользуйтесь.

Где результаты?


Стабильный рост поискового трафика, низкий показатель отказов

С 2014 по 2017 год Яндекс.Метрика показывает стабильный рост трафика с учетом сезонности:

image

Ниже показан рост поискового трафика в мае (за последние 4 года):

image

Доля поискового трафика за последний год составляет 72,6%:

image

Показатель отказов на сайте уменьшается каждый год, а глубина просмотров растет:

image

Рост продаж на 60%, валовой прибыли – на 30%, доли рынка – на 3,5%

image

«Продажи растут в соответствии с показателями аналитики сайта как в Москве, так и в других регионах Центральной России. С 2014 по 2017 годы продажи выросли на 60%, валовая прибыль – на 30%, мы увеличили долю рынка на 3,5%. Прирост объемов работ составил 250%. Кроме того, на 40% выросло производство в регионах»
Алексей Красников, генеральный директор ГК «Забава»

Вопрос к читателям


Друзья, у меня есть возможность делиться не только историями создания и продвижения сайтов. Есть кейсы внедрения CRM-систем, лайвхаки по ведению контекстной рекламы, ретаргетингу. Что вам интересно почитать в первую очередь? Напишите в комментариях.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332830/


Метки:  

[Перевод] ES8 вышел и вот его основные новые возможности

Среда, 12 Июля 2017 г. 09:21 + в цитатник
Новые фичи восьмой редакции EcmaScript.

image

EcmaScript 8 или EcmaScript 2017 был официально выпущен комитетом TC39 в конце июня. Кажется, мы много говорили о EcmaScript в прошлом году и это было не просто так. На данный момент стандартом является выпуск новой спецификации раз в год. ES6 был опубликован в 2015, а ES7 в 2016, но кто-нибудь помнит, когда вышел ES5? Это было в 2009 году, до волшебного взлета JavaScript.

Итак, мы следим за изменениями в разработке стабильного языка JavaScript, и теперь нам надо добавить ES8 в свой лексикон.

ES2017 (the 8th edition of the JavaScript Spec) was officially released and published yesterday! https://t.co/1ITn5bzaqj

— Kent C. Dodds (@kentcdodds) June 28, 2017


Суровые люди, могут сделать глубокий вдох и прочитать web или PDF версию спецификации. Для остальных в этой статье мы рассмотрим главные новые возможности ES8 с примерами.

Паддинги строк


В этом разделе добавлены две функции в объект String: padStart и padEnd. Как можно понять из их названия, цель этих функций – дополнить строку от начала или конца так, что в результате строка достигнет указанной длины. Вы можете дополнить строку определёнными символами или пробелами по умолчанию.
Вот декларация функций:

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])


Как видно, первый параметр этих функций targetLength – это полная длина итоговой строки. Второй опциональный параметр padString – это строка для дополнения исходной строки. По умолчанию – пробел.

'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // '  es8'
'es8'.padStart(6, 'woof');  // 'wooes8'
'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8'
'es8'.padEnd(2);          // 'es8'
'es8'.padEnd(5);          // 'es8  '
'es8'.padEnd(6, 'woof');  // 'es8woo'
'es8'.padEnd(14, 'wow');  // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');     // 'es86666'


image

Object.values и Object.entries


Метод Object.values возвращает массив собственных перечисляемых свойств переданного объекта в том же порядке, который предоставляет цикл for in.
Декларация функции тривиальна:

Object.values(obj)


Параметр obj – исходный объект для операции. Это может быть объект или массив (который является объектом с такими индексами [10, 20, 30] -> { 0: 10, 1: 20, 2: 30 } ).

const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]

const obj = ['e', 's', '8']; // same as { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']

// когда мы используем числовые ключи, значения возвращаются 
// в порядке сортировки по ключам
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']
Object.values('es8'); // ['e', 's', '8']


image

Метод Object.entries возвращает массив собственных перечисляемых свойств переданного объекта парами [ключ, значение] в том же порядке, как и Object.values.
Декларация тривиальна:

const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]

const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]

const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]
Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]


image

Object.getOwnPropertyDescriptors


Метод getOwnPropertyDescriptors возвращает дескрипторы собственных свойств указанного объекта. Дескриптор собственного свойства это тот, который определён прямо у объекта, а не унаследован от его прототипа.
Декларация функции такая:

Object.getOwnPropertyDescriptor(obj, prop)


obj – исходный объект и prop – имя свойства, дескриптор которого нужно получить. Возможные ключи в результате: configurable, enumerable, writable, get, set и value.

const obj = { get es8() { return 888; } };
Object.getOwnPropertyDescriptor(obj, 'es8');
// {
//   configurable: true,
//   enumerable: true,
//   get: function es8(){}, // функция геттер
//   set: undefined
// }


Данные дескрипторов очень важны для таких продвинутых фич, как декораторы.

image

Лишние запятые в списке параметров функции и вызове


Возможность указать лишние запятые в параметрах функции позволяет не получать ошибку (SyntaxError) когда мы добавили запятую в конце списка:

function es8(var1, var2, var3,) {
  // ...
}


Как и в объявлении функции, этот синтаксис можно использовать и при её вызове:

es8(10, 20, 30,);


Эта возможность была вдохновлена лишними запятыми в литералах объекта { x: 1, } и литералах массива [10, 20, 30,].

Асинхронные функции


Объявление async function определяет асинхронную функцию, которая возвращает объект AsyncFunction. Внутреннее устройство асинхронных функций работает подобно генераторам, но они не транслируются в функции генератора.

function fetchTextByPromise() {
  return new Promise(resolve => { 
    setTimeout(() => { 
      resolve("es8");
    }, 2000);
  });
}
async function sayHello() { 
  const externalFetchedText = await fetchTextByPromise();
  console.log(`Hello, ${externalFetchedText}`); // Hello, es8
}
sayHello();


Вызов sayHello выведет Hello, es8 через 2 секунды.

console.log(1);
sayHello();
console.log(2);


Напечатает:

1 // сразу
2 // сразу
Hello, es8 // через 2 секунды


Это связано с тем, что вызов функции не блокирует поток выполнения.

Обратите внимание, что async function всегда возвращает промис и ключевое слово await может использоваться только в функциях с ключевым словом async.

image

Разделяемая память и атомарные операции



Когда память разделяемая, множество потоков может читать и писать те же данные в памяти. Атомарные операции позволяют убедиться, что предсказуемые значения записаны и прочитаны, что операции завершены до начала следующих операций. В этом разделе представлен новый конструктор SharedArrayBuffer и объект Atomics со статическими методами.

Объект Atomics – это набор статических методов как Math, так что мы не сможем вызвать его конструктор. Примеры статических методов этого объекта:
  • add / sub — добавление / вычитание значения из значения в указанной позиции
  • and / or / xor — побитовое «И» / побитовое «ИЛИ» / исключающее «ИЛИ»
  • load — получение значения в указанной позиции


image

И один момент на следующий год в ES9 – снятие ограничений для шаблонных литералов



С тегированными шаблонными строками (ES6) мы можем делать такие штуки, как объявление функций для парсинга шаблонов и возвращения значений согласно какой-то логике.

const esth = 8;
helper`ES ${esth} is `;
function helper(strs, ...keys) {
  const str1 = strs[0]; // ES
  const str2 = strs[1]; // is
  let additionalPart = '';
  if (keys[0] == 8) { // 8
    additionalPart = 'awesome';
  }
  else {
    additionalPart = 'good';
  }
  
  return `${str1} ${keys[0]} ${str2} ${additionalPart}.`;
}


Вернется значение -> ES 8 is awesome.
И для esth равным 7 вернётся -> ES 7 is good.

Но существуют ограничения для шаблонов, которые содержат подстроки \u или \x. ES9 решит проблему экранирования. Читайте подробнее на сайте MDN или в документе TC39.

image

Заключение


JavaScript уже в продакшене, но всегда обновляется. Процесс принятия новых возможностей в спецификацию очень организован и устойчив. На последнем этапе этот функционал подтверждается комитетом TC39 и реализуется основными разработчиками. Большинство из них уже реализовано в языке Typescript, браузерах или разных полифиллах, так что вы можете пробовать их уже сейчас.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332900/


Метки:  

GitLab CI для непрерывной интеграции и доставки в production. Часть 2: преодолевая трудности

Среда, 12 Июля 2017 г. 09:00 + в цитатник

Эта статья продолжает первую часть, содержащую подробное описание нашего пайплайна:

image
… и рассказывает о проблемах, с которыми мы столкнулись для его реализации, и их решении.

Итак, я остановился на том, что созданный .gitlab-ci.yml не позволяет реализовать пайплайн в полной мере, поскольку GitLab CI не предоставляет директив для разделения задач по пользователям и для описания зависимостей выполнения задач от статуса выполнения других задач, а также не позволяет разрешить модификацию .gitlab-ci.yml только для отдельных пользователей.

1. Защита .gitlab-ci.yml от изменений


Любой пользователь, который имеет разрешение на git push, может изменить .gitlab-ci.yml и сломать production. Эту проблему обсуждают в тикетах GitLab: как минимум — в #24794 и #20826 с подачи нашего коллеги.

Пока сложно сказать, будет ли когда-либо реализована защита из коробки, но на данный момент мы реализовали её упрощённый вариант с помощью небольшого патча: push’ить коммиты с изменениями в .gitlab-ci.yml могут только некоторые пользователи — обычно это команда DevOps, т.к. сборка и развёртывание в их зоне ответственности.

Помимо применения патча потребуется добавить boolean-столбец ci_admin в таблицу с пользователями. Кому в столбце установлено true, тот может делать git push с изменениями в .gitlab-ci.yml.

2. Переменные для скрипта задачи


Вторая проблема, которую получилось решить довольно легко, — переменные среды GITLAB_USER_ID и GITLAB_USER_EMAIL для скрипта задачи с идентификатором пользователя и его почтой. Эти переменные можно использовать, чтобы определить, может ли пользователь запустить задачу. Реализовано как решение тикета #21825, принято в основную ветку (upstream) и доступно в GitLab CI начиная с версии 8.12:


3. Зависимости между стадиями


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

То есть, во-первых, автоматические задачи запускаются только в момент создания пайплайна, а во-вторых, нельзя сделать такой процесс, когда успешное выполнение ручной задачи запустило бы автоматические задачи, расположенные дальше в пайплайне. Документация по сути описывает поведение автоматических задач. Ручные же задачи живут «сами по себе» и могут быть запущены в любой момент, независимо от статуса выполнения задач на предыдущих стадиях.

На этот счёт есть несколько тикетов, где предлагается изменить поведение ручных и автоматических задач:

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

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

Рассмотрим подробнее на двух примерах. Сначала на примере стадий testing и staging:



По описанию пайплайна, задачи развёртывания на окружения тестировщиков (deploy to qa-*) можно запускать только после выполнения всех тестов, а остальные задачи такой зависимости не имеют. Для реализации этой логики в конце успешного выполнения тестов делается touch файла с именем задачи, а в начале выполнения задач deploy to qa-*, на стадии staging, проверяется наличие этих файлов.

Вот для примера листинги задач test integration и deploy to qa-1:

test integration:
  stage: testing
  tags: [deploy]
  script:
    - mkdir -p .ci_status
    - echo "test integration"
    - touch .ci_status/test_integration
  artifacts:
    paths:
      - .ci_status/

deploy to qa-1:
  tags: [deploy]
  stage: staging
  when: manual
  script:
    - if [ ! -e .ci_status/test_unit -o ! -e .ci_status/test_integration -o ! -e .ci_status/test_selenium ]; then echo "Нужно успешное выполнение всех тестов"; exit 1; fi
    - echo "execute job ${CI_BUILD_NAME}"
    - touch .ci_status/deploy_to_qa_1
  artifacts:
    paths:
      - .ci_status/

Добавилась директива artifact, которая определяет пути в репозитории, сохраняемые GitLab CI в архив после выполнения задачи и разархивируемые перед выполнением следующей задачи. Чтобы не перечислять все файлы, указывается директория .ci_status, которую не помешает создать во время выполнения задачи (mkdir -p).

Исходник: файл .gitlab-ci.yml с зависимостью стадии staging от testing доступен здесь.

Второй пример немного сложнее — это зависимость стадии production от стадии approve:



Задачи approve и not approve создают файлы, которые проверяет задача production. Это можно сделать так же, как и в предыдущем примере, но хочется, чтобы задачи NOT approve и approve работали как переключатель. Такой работе мешает факт, что нельзя удалить файлы артефактов от другой задачи. Поэтому задачи не просто создают файл, а пишут в него timestamp. В начале выполнения задачи deploy to production выполняется проверка: если в файле от задачи approve timestamp больше, то можно продолжать, если нет — задача завершается с ошибкой.

approve:
  script:
    - mkdir -p .ci_status
    - echo $(date +%s) > .ci_status/approved
  artifacts:
    paths:
      - .ci_status/

NOT approve:
  script:
    - mkdir -p .ci_status
    - echo $(date +%s) > .ci_status/not_approved
  artifacts:
    paths:
      - .ci_status/

deploy to production:
  script:
    - if [[ $(cat .ci_status/not_approved) > $(cat .ci_status/approved) ]]; then echo "Нужно разрешение от релиз-инженера"; exit 1; fi
    - echo "deploy to production!"

После выполнения задачи appove успешно выполняется deploy to production:



После выполнения задачи NOT approve следующая за ней задача deploy to production завершается с ошибкой:



Исходники:

Что дальше?


Осталось не озвученным требование разрешать отдельные задачи только некоторым пользователям. На данном этапе стало понятно, как это можно реализовать: нужен REST API, который можно будет запросить через curl с передачей переменных GITLAB_USER_ID и GITLAB_USER_EMAIL. Создание такого REST API выходит за рамки данной статьи.

В приведённых примерах скрипт, проверяющий зависимости, хранится в .gitlab-ci.yml. Это очень неудобно, если проектов много и нужно что-то поправить, например, если появится новое окружение для qa или окружений для pre-production станет больше. Мы это решили с помощью вынесения скриптов в один внешний скрипт, который не хранится в каждом репозитории, а устанавливается на машины с раннерами.

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

Обычно в пайплайне не так много вариаций задач, наш скрипт знает о трёх:
  1. инструкции для сборки,
  2. инструкции для разворачивания,
  3. инструкции approve и not approve.

Инструкции тоже получают переменные среды и могут подстраиваться под конкретную задачу. Так как вариантов сборки и развёртывания в окружениях великое множество, да и количество проектов в GitLab тоже у всех отличается, то приводить реализацию такого скрипта считаю излишним. Впрочем, если есть вопросы — давайте обсудим в комментариях.

Вместо заключения


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



P.S. Не забывайте про проверку .gitlab-ci.yml по адресу https://мой-гитлаб/ci/lint. Поможет сэкономить время!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332842/


Asterisk. На этот раз в качестве системы трансляции фоновой музыки с возможностью экстренного оповещения

Среда, 12 Июля 2017 г. 09:00 + в цитатник
image

Добрый день, уважаемые жители хаба Asterisk.

Не знаю, как вам, а мне в последнее время безумно интересно применение Asterisk не в качестве стандартной АТС.

В предыдущем посте Asterisk исполнял обязанности охранника на парковке. На этот раз Asterisk в моей конфигурации проигрывал фоновую музыку в торговом павильоне и в случае возникновения экстренной ситуации (или при необходимости сделать какое-то объявление) выступал в роли системы оповещения.

Подробности под катом.



Пару недель назад возникла у меня одна интересная задачка, а именно развернуть систему проигрывания фоновой музыки, одной из функций которой должна быть возможность «вклиниться» в проигрывание звукового файла, сделать объявление, после чего продолжить воспроизведение музыки с того же места.

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

Также были рассмотрены существующие девайсы SIP-оповещения. Вот тут уже интереснее: они регистрируются на Asterisk как extension'ы, умеют вещать мультикаст, умеют выставлять приоритет разным звуковым потокам. В России можно найти IP-SIP динамики Cyberdata, которые были весьма подробно рассмотрены grigly в этом топике. У динамиков появилась новая прошивка, интерфейс немного освежили. Имея возможность «поиграться» с данным девайсом, грех было ей не воспользоваться:)

Итак, приступим.

Описывать установку Asterisk мы, разумеется, не будем: в сети полно качественных мануалов. Единственное, что добавлю: давно уже сделал для себя docker-compose с asterisk, mysql, php-apache, cdr-viewer, что делает деплой asterisk практически моментальным. Планирую описать это в следующем топике).

Создаем на Asterisk парочку extension'ов (по умолчанию /etc/asterisk/sip.conf):
Скрытый текст
[tmpl](!)
type = peer
host = dynamic
canreinvite=no
dtmfmode = rfc2833
insecure = invite
nat = force_rport,comedia
call-limit=2
qualify = yes
context = from-internal
disallow=all
allow=alaw
allow=ulaw
directmedia=no
[780](tmpl)
defaultuser=780
secret=780
callerid="Dispatcher" <780>
[790](tmpl)
defaultuser=790
secret=790
callerid="Speaker1" <790>
[800](tmpl)
defaultuser=800
secret=800
callerid="Speaker2" <800>



Пишем простейший диалплан Asterisk для тестов (по умолчанию /etc/asterisk/extensions.conf):
Скрытый текст
exten => _XXX,1,NoOp(Testing calls to speakers. Dialing ${EXTEN} from ${CALLERID})
same => n,Page(SIP/${EXTEN},qA(hello-world))
same => n,Hangup()



Переходим к настройке динамика.

Находим его в нашей локальной сети (по умолчанию устройство настроено на dhcp):


Настраиваем sip extension (для входа на динамик логин/пароль по умолчанию admin/admin):



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

И настраиваем мультикаст:



Обратите внимание на надпись: «SIP calls are considered priority 4.5. Priority 9 is the highest and 0 is the lowest. » Для того чтобы мультикаст-вещание прерывалось при звонке на динамик, настройки мультикаст должны иметь приоритет 4 и ниже.

Теперь настроим мультикаст-вещание на сервере asterisk. Я пользовался утилитой ffmpeg.
Устанавливаем:
sudo apt-get install ffmpeg


Запускаем вещание (я взял плейлист с vocaltrance.fm (не реклама), разумеется, можно выбрать любой):
ffmpeg -re -i 176.9.36.203:8000/vocaltrance_128 -filter_complex 'aresample=8000,asetnsamples=n=160' -acodec pcm_alaw -ac 1 -vn -f rtp udp://236.0.0.1:2000?buffer_size=10000000?fifo_size=1000000


И слушаем:)





У этого варианта реализации помимо очевидных преимуществ есть один большой недостаток, а именно цена, которая для данного динамика составляет порядка 400$ долларов за штуку.
Чтобы понизить стоимость системы, я решил использовать обычные колонки (у меня под рукой были
вот такие
и raspberry pi. ( Стоимость динамика и raspberry составила ~ 50$).

Итак, приступим.

Устанавливаем на raspberry дистрибутив raspbx, который представляет из себя готовый образ freepbx для архитектуры ARM. (Для установки понадобится флешка размеров от 4Gb.)

Установка тривиальна: скачиваем, берем подходящую флешку, отмонтируем партицию, пишем образ через dd:
sudo dd if=path_of_your_image.img of=/dev/diskn bs=1M



Находим в сети логин/пароль для ssh root/raspberry, для web — admin/admin.

Создаем транк до нашего «головного» Asterisk:
[rasp]
type=peer
host=192.168.1.254 ;;ip "головного" Asterisk
qualify=yes
insecure = port,invite
directmedia=no
context=speakers
canreinvite=no
disallow=all
allow=alaw
allow=ulaw




На «головном» Asterisk создаем такой же транк, только указываем ему ip raspberry.

Звонки на raspberry будут отправляться в chan_alsa (module load chan_alsa.so, если по дефолту не загружен).

Для проигрывания музыки используем omxplayer (sudo apt-get -y install omxplayer).

Чтобы звонки прерывали воспроизведение музыки, напишем простенький диалплан:
Скрытый текст
[speakers]
exten => s,1,System(killall /usr/bin/omxplayer.bin)
same => n,Wait(2)
same => n,Dial(console/sdp)
same => n,Hangup()
exten => h,1,System(omxplayer -o local rtp://@236.0.0.1:2000)



Для звонка с «головного» Asterisk добавим к исходному диалплану:

exten => 1000,1,Dial(SIP/rasp/s,60,Tt)
same => n,Hangup()

И наслаждаемся результатом:)





Заключение

В результате мы имеем вполне работающую систему оповещения / трансляции фоновой музыки, контролируемую Asterisk'ом, с несколькими вариантами оконечных устройств. Варианты использования прочих устройств приветствуются в комментариях. Спасибо за внимание.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/268047/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1047 1046 [1045] 1044 1043 ..
.. 1 Календарь