РЭДЛАЙН
Лучшие решения для Вас и Вашего бизнеса!
На нашем сайте вы можете получить информацию о веб-разработке, обслуживании и продвижении сайта. Интернет-маркетинге. SEO (поисковой оптимизации). Контекстной и медийной рекламе в Интернете. SMM. Регистрации доменов и хостинговых услугах. И современном дизайне сайтов. Вообщем того что касается веб-разработки, а также много другой полезной информации из мира интернета, бизнеса и интернет-технологий...
Создаем доступные и современные сайты, которые работают! Обслуживаем и эффективно продвигаем интернет-проекты с 2006 года!
Главная Блоги Пишем упаковщик по шагам. Шаг второй. Пакуем.


Пишем упаковщик по шагам. Шаг второй. Пакуем.

Предыдущий шаг здесь

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

И мы продолжаем написание собственного упаковщика. В этом шаге пора переходить непосредственно к упаковке PE-файла. Я достаточно давно выкладывал простенький упаковщик, который был малоэффективным по двум причинам: во-первых, он использовал стандартные Windows-функции для упаковки и распаковки данных, обладающие достаточно низкой степенью сжатия и скоростью, во-вторых, паковались все секции PE-файла по отдельности, что не очень-то оптимально. В этот раз я сделаю по-другому. Мы будем считывать данные всех секций сразу, слеплять их в один кусок и упаковывать. В результирующем файле, таким образом, будет только одна секция (на самом деле две, потом поясню, почему), в которой мы сможем разместить и ресурсы, и код распаковщика, и сжатые данные, и вспомогательные таблицы. Мы получаем некоторый выигрыш, потому что не нужно тратить размер на файловое выравнивание, кроме того, алгоритм LZO явно более эффективен, чем RtlCompressBuffer, во всех отношениях.

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

#pragma pack(push, 1)//Структура, хранящая информацию об упакованной секцииstruct packed_section
{char name[8];//Имя секции
  DWORD virtual_size;//Виртуальный размер
  DWORD virtual_address;//Виртуальный адрес (RVA)
  DWORD size_of_raw_data;//Размер "сырых" данных
  DWORD pointer_to_raw_data;//Файловое смещение сырых данных
  DWORD characteristics;//Характеристики секции};

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

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

//Структура, хранящая информацию об упакованном файлеstruct packed_file_info
{
  BYTE number_of_sections;//Количество секций в оригинальном файле
  DWORD size_of_packed_data;//Размер упакованных данных
  DWORD size_of_unpacked_data;//Размер оригинальных данных};#pragma pack(pop)

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

Идем дальше. Перед упаковкой желательно бы просчитать энтропию секций файла, чтобы определить, есть ли смысл его упаковывать, или он уже сжат по максимуму. Моя библиотека предоставляет такую возможность. Кроме того, стоит проверить, не передали ли нам .NET-бинарник - такие мы упаковывать не будем.

...
  try{//Пытаемся открыть файл как 32-битный PE-файл//Последние два аргумента false, потому что нам не нужны//"сырые" данные привязанных импортов файла и //"сырые" данные отладочной информации//При упаковке они не используются, поэтому не загружаем эти данные
    pe32 image(file, false, false);
 
    //Проверим, не .NET ли образ нам подсунулиif(image.is_dotnet()){
      std::cout<<".NEt image cannot be packed!"<< std::endl;return-1;}
 
    //Просчитаем энтропию секций файла, чтобы убедиться, что файл не упакован{
      std::cout<<"Entropy of sections: ";double entropy = image.calculate_entropy();
      std::cout<< entropy << std::endl;//На wasm.ru есть статья, в которой говорится,//что у PE-файлов нормальная энтропия до 6.8//Если больше, то файл, скорее всего, сжат//Поэтому (пока что) не будем упаковывать файлы//с высокой энтропией, в этом мало смыслаif(entropy >6.8){
        std::cout<<"File has already been packed!"<< std::endl;return-1;}}
...

Перейдем к упаковке секций. Добавим в начало main.cpp строку #include <string> - строки нам пригодятся для формирования блоков данных (они располагают данные последовательно, и мы сможем прямо из строки записывать их в файл). Можно было использовать и векторы (vector), однако особой разницы нет.

Для начала необходимо произвести инициализацию библиотеки LZO:

//Инициализируем библиотеку сжатия LZOif(lzo_init()!= LZO_E_OK){
      std::cout<<"Error initializing LZO library"<< std::endl;return-1;}

Переходим к считыванию секций файла:

    std::cout<<"Reading sections..."<< std::endl;
 
    //Получаем список секций PE-файлаconst pe_base::section_list& sections = image.get_image_sections();if(sections.empty()){//Если у файла нет ни одной секции, нам нечего упаковывать
      std::cout<<"File has no sections!"<< std::endl;return-1;}

Переходим непосредственно к упаковке файла.

//Структура базовой информации о PE-файле
    packed_file_info basic_info ={0};//Получаем и сохраняем изначальное количество секций
    basic_info.number_of_sections= sections.size();
 
    //Строка, которая будет хранить последовательно//структуры packed_section для каждой секции
    std::string packed_sections_info;
 
    {//Выделим в строке необходимое количество памяти для этих стркуткр
      packed_sections_info.resize(sections.size()*sizeof(packed_section));
 
      //"Сырые" данные всех секций, считанные из файла и слепленные воедино
      std::string raw_section_data;//Индекс текущей секцииunsignedlong current_section =0;
 
      //Перечисляем все секцииfor(pe_base::section_list::const_iterator it = sections.begin(); it != sections.end();++it, ++current_section){//Ссылка на очередную секциюconst pe_base::section& s =*it;
 
        {//Создаем структуру информации//о секции в строке и заполняем ее
          packed_section& info
            =reinterpret_cast<packed_section&>(packed_sections_info[current_section *sizeof(packed_section)]);
 
          //Характеристики секции
          info.characteristics= s.get_characteristics();//Указатель на файловые данные
          info.pointer_to_raw_data= s.get_pointer_to_raw_data();//Размер файловых данных
          info.size_of_raw_data= s.get_size_of_raw_data();//Относительный виртуальный адрес секции
          info.virtual_address= s.get_virtual_address();//Виртуальный размер секции
          info.virtual_size= s.get_virtual_size();
 
          //Копируем имя секции (оно максимально 8 символов)memset(info.name, 0, sizeof(info.name));memcpy(info.name, s.get_name().c_str(), s.get_name().length());}
 
        //Если секция пустая, переходим к следующейif(s.get_raw_data().empty())continue;
 
        //А если не пустая - копируем ее данные в строку//с данными всех секций
        raw_section_data += s.get_raw_data();}
 
      //Если все секции оказались пустыми, то паковать нечего!if(raw_section_data.empty()){
        std::cout<<"All sections of PE file are empty!"<< std::endl;return-1;}
 
      //Будем упаковывать оба буфера, слепленные вместе//(читайте ниже)
      packed_sections_info += raw_section_data;}

Немного поясню по коду выше. Мы создали два буфера - packed_sections_info и raw_section_data. Не обращайте внимания, что это строки (std::string), они могут хранить бинарные данные. Первый буфер хранит идущие подряд структуры packed_section, создаваемые и заполняемые нами для всех имеющихся в PE-файле секций. Второй хранит сырые данные всех секций, слепленные вместе. Мы сможем эти данные после распаковки разделить и распихать по секциям заново, потому что информация о размере файловых данных секций хранится у нас в первом буфере и будет доступна распаковщику. Идем дальше - нужно полученный буфер raw_section_data упаковать. Можно вместе с ним упаковать и буфер packed_sections_info - пожалуй, так и сделаем. Для этого конкатенируем строки (читай: бинарные буферы) packed_sections_info и raw_section_data - это сделано в предыдущем блоке кода.

Далее мы займемся созданием новой секции PE-файла, в которой разместим наши упакованные данные:

//Новая секция
    pe_base::section new_section;//Имя - .rsrc (пояснение ниже)
    new_section.set_name(".rsrc");//Доступна на чтение, запись, исполнение
    new_section.readable(true).writeable(true).executable(true);//Ссылка на сырые данные секции
    std::string& out_buf = new_section.get_raw_data();

Итак, мы создали новую секцию (но пока не добавили ее к PE-файлу). Почему я назвал ее .rsrc? Это сделано по одной простой причине. Все файлы, имеющие ресурсы, располагают их в секции с именем .rsrc. Главная иконка файла и информация о версии также хранятся в ресурсах. Увы, проводник Windows умеет считывать иконку файла и отображать ее ТОЛЬКО в том случае, если секция, хранящая ресурсы, называется .rsrc. Эту штуку вроде бы как поправили в последних версиях и сервис-паках Windows, но лучше перестраховаться. Мы пока что ресурсами не занимаемся, поэтому название дано на будущее.

Следующий шаг - сжатие данных. Немного низкоуровневый момент... И тут нам понадобится библиотека Boost. У вас ее еще нет? Пора скачать, установить и собрать! Тем более, делается это очень просто. Но для того класса из этой библиотеки, который я дальше собираюсь использовать, даже и собирать ее не надо. Просто скачайте библиотеку, распакуйте ее в какую-нибудь директорию, например, C:\boost, и укажите в include-директориях в проекте путь к заголовочным файлам буста, например C:\boost\boost. Если мне в дальнейшем из буста потребуется класс, требующий сборки, я поясню, как это делается.

Добавим к заголовкам main.cpp строку #include <boost/scoped_array.hpp>. Далее упаковываем данные.

//Создаем "умный" указатель//и выделяем необходимую для сжатия алгоритму LZO память//Умный указатель в случае чего автоматически//эту память освободит//Мы используем тип lzo_align_t для того, чтобы//память была выровняна как надо//(из документации к LZO)
    boost::scoped_array<lzo_align_t> work_memory(new lzo_align_t[LZO1Z_999_MEM_COMPRESS]);
 
    //Длина неупакованных данных
    lzo_uint src_length = packed_sections_info.size();//Сохраним ее в нашу структуру информации о файле
    basic_info.size_of_unpacked_data= src_length;
 
    //Длина упакованных данных//(пока нам неизвестна)
    lzo_uint out_length =0;
 
    //Необходимый буфер для сжатых данных//(длина опять-таки исходя из документации к LZO)
    out_buf.resize(src_length + src_length /16+64+3);
 
    //Производим сжатие данных
    std::cout<<"Packing data..."<< std::endl;if(LZO_E_OK !=
      lzo1z_999_compress(reinterpret_cast<constunsignedchar*>(packed_sections_info.data()),
      src_length,
      reinterpret_cast<unsignedchar*>(&out_buf[0]),
      &out_length,
      work_memory.get())){//Если что-то не так, выйдем
      std::cout<<"Error compressing data!"<< std::endl;return-1;}
 
    //Сохраним длину упакованных данных в нашу структуру
    basic_info.size_of_packed_data= out_length;//Ресайзим выходной буфер со сжатыми данными по//результирующей длине сжатых данных, которая//теперь нам известна
    out_buf.resize(out_length);//Собираем буфер воедино, это и будут//финальные данные нашей новой секции
    out_buf =//Данные структуры basic_info
      std::string(reinterpret_cast<constchar*>(&basic_info), sizeof(basic_info))//Выходной буфер+ out_buf;
 
    //Проверим, что файл реально стал меньшеif(out_buf.size()>= src_length){
      std::cout<<"File is incompressible!"<< std::endl;return-1;}

Теперь осталось удалить уже ненужные нам секции PE-файла и добавить в него нашу новую секцию:

{//Сначала получим ссылку на самую первую//существующую секцию PE-файлаconst pe_base::section& first_section = image.get_image_sections().front();//Установим виртуальный адрес для добавляемой секции (читай ниже)
      new_section.set_virtual_address(first_section.get_virtual_address());
 
      //Теперь получим ссылку на самую последнюю//существующую секцию PE-файлаconst pe_base::section& last_section = image.get_image_sections().back();//Посчитаем общий размер виртуальных данных
      DWORD total_virtual_size =//Виртуальный адрес последней секции
        last_section.get_virtual_address()//Выровненный виртуальный размер последней секции+ pe_base::align_up(last_section.get_virtual_size(), image.get_section_alignment())//Минус виртуальный размер первой секции- first_section.get_virtual_address();
 
      //Удаляем все секции PE-файла
      image.get_image_sections().clear();
 
      //Изменяем файловое выравнивание, если вдруг оно было//больше, чем 0x200 - это минимально допустимое//для выровненных PE-файлов
      image.realign_file(0x200);
 
      //Добавляем нашу секцию и получаем ссылку на//уже добавленную секцию с пересчитанными адресами и размерами
      pe_base::section& added_section = image.add_section(new_section);//Устанавливаем для нее необходимый виртуальный размер
      image.set_section_virtual_size(added_section, total_virtual_size);}

Что же здесь произошло? Поясню подробнее. Сначала мы определили виртуальный адрес самой первой секции в PE-файле (об этом ниже). После этого мы определили общий виртуальный размер всех секций. Так как виртуальный размер последующей секции равен виртуальному адресу + выровненному виртуальному размеру предыдущей, то, узнав виртуальный адрес и размер последней в файле секции, мы получили виртуальный суммарный размер всех секций плюс адрес самой первой секции. Вычтя из этого числа тот самый виртуальный адрес первой секции, получаем чистый виртуальный размер всех секций вместе взятых. Это, кстати, можно было сделать гораздо проще - вызвав функцию image.get_size_of_image(), которая вернула бы, по сути, то же самое, но из заголовка PE-файла, ну да ладно. Далее мы удалили все существующие секции PE-файла. После этого добавили нашу секцию в PE-файл и получили ссылку на добавленную секцию с пересчитанными адресами и размерами (после добавления мы работаем именно с этой ссылкой). Далее мы должны оставить себе достаточное количество памяти, чтобы потом в нее распаковать все секции - поэтому мы и меняем виртуальный размер свежедобавленной секции на общий размер всех ранее существовавших секций. Виртуальный адрес добавленной секции будет вычислен автоматически по умолчанию. Нас это не очень устраивает - нам необходимо, чтобы область в памяти, занимаемая нашей секцией, полностью совпала с областью, которую занимали все секции оригинального файла. Моя библиотека позволяет явно указать виртуальный адрес секции, если она будет первой в файле (т.е. до ее добавления никаких других секций не существует). Это как раз наша ситуация. Именно поэтому мы и определили виртуальный адрес первой секции и установили его для нашей новой секции.

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

Однако, одной секцией мы не обойдемся и нам придется создать и добавить еще одну. Зачем? - спросите вы. Ответ прост: первая секция после распаковки будет содержать данные всех секций оригинального файла. А нам еще надо где-то разместить распаковщик. Вы скажете: ну так помести его в конец секции. Но тогда он будет перезаписан при распаковке данными оригинального файла! Можно, конечно, действительно разместить его в той же самой секции, и перед самой распаковкой выделить память (с помощью VirtualAlloc или как-то еще) и скопировать туда тело распаковщика, и исполнять его уже оттуда. Но эту память потом нам нужно будет как-то освободить. И если мы это сделаем из нее самой, то произойдет падение приложения: память освобождена, и регистр процессора eip, указывающий на текущую исполняемую ассемблерную команду, указывает вникуда. Словом, без дополнительной секции не обойтись. Если вы посмотрите на тот же UPX или Upack, то увидите, что они тоже имеют по 2-3 секции.

{//Новая секция
      pe_base::section unpacker_section;//Имя - kaimi.ru
      unpacker_section.set_name("kaimi.ru");//Доступна на чтение и исполнение
      unpacker_section.readable(true).executable(true);//В будущем тут будет код распаковщика и что-то еще
      unpacker_section.get_raw_data()="Nothing interesting here...";//Добавляем и эту секцию
      image.add_section(unpacker_section);}

Переходим к следующему шагу. Немного поиздеваемся над PE-файлом:

//Удалим все часто используемые директории//В дальнейшем мы будем их возвращать обратно//и корректно обрабатывать, но пока так//Оставим только импорты (и то, обрабатывать их пока не будем)
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_BASERELOC);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_EXPORT);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_IAT);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_RESOURCE);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_SECURITY);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_TLS);
    image.remove_directory(IMAGE_DIRECTORY_ENTRY_DEBUG);
 
    //Урезаем таблицу директорий, удаляя все нулевые//Урезаем не полностью, а минимум до 12 элементов, так как в оригинальном//файле могут присутствовать первые 12 и использоваться
    image.strip_data_directories(16-4);//Удаляем стаб из заголовка, если какой-то был
    image.strip_stub_overlay();

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

Далее я обрезал таблицу директорий, так как у нас большинство из них теперь удалено, и удалил стаб из заголовка (обычно в нем лежит DOS stub и Rich-сигнатуры MSVC++, это нам не нужно). Таблицу директорий уменьшаем минимум до 12 элементов, не меньше. Элементы с 1 по 12 могут присутствовать в оригинальном файле и их придется восстановить. Можно было бы, конечно, оставить и самый минимум элементов в таблице, но выигрыша в размере это не даст, зато кода в распаковщике прибавится, если вдруг нам придется расширять таблицу обратно. Почему урезаем таблицу именно до 12 элементов? Потому что четыре последних точно не нужны PE-файлу для успешного запуска, и без них можно спокойно обойтись. Можно было бы еще динамически проверять, есть ли у файла 12-я (Configuration directory), 11-я (TLS directory) и т.д директории, и если нет, то еще больше урезАть таблицу директорий, но, повторюсь, смысла особого в этом нет.

Последнее, что нам остается сделать - сохранить упакованный файл под новым именем:

//Создаем новый PE-файл//Вычислим имя переданного нам файла без директории
    std::string base_file_name(argv[1]);
    std::string dir_name;
    std::string::size_type slash_pos;if((slash_pos = base_file_name.find_last_of("/\\"))!= std::string::npos){
      dir_name = base_file_name.substr(0, slash_pos +1);//Директория исходного файла
      base_file_name = base_file_name.substr(slash_pos +1);//Имя исходного файла}
 
    //Дадим новому файлу имя packed_ + имя_оригинального_файла//Вернем к нему исходную директорию, чтобы сохранить//файл туда, где лежит оригинал
    base_file_name = dir_name +"packed_"+ base_file_name;//Создадим файл
    std::ofstream new_pe_file(base_file_name.c_str(), std::ios::out| std::ios::binary| std::ios::trunc);if(!new_pe_file){//Если не удалось создать файл - выведем ошибку
      std::cout<<"Cannot create "<< base_file_name << std::endl;return-1;}
 
    //Пересобираем PE-образ//Урезаем DOS-заголовок, накладывая на него NT-заголовки//(за это отвечает второй параметр true)//Не пересчитываем SizeOfHeaders - за это отвечает третий параметр
    image.rebuild_pe(new_pe_file, true, false);
 
    //Оповестим пользователя, что файл упакован успешно
    std::cout<<"Packed image was saved to "<< base_file_name << std::endl;

В этой части кода ничего сложного не происходит, все должно быть более-менее понятно из комментариев. Итак, это все, что мы делаем в этом шаге. Шаг получился более чем насыщенным, и вам есть, о чем подумать. Естественно, упакованный файл не будет запускаться, потому что у него нет распаковщика, мы не обрабатываем импорты и не правим точку входа и еще много-много всего... Однако мы можем оценить степень сжатия и проверить в каком-нибудь просмотрщике PE-файлов (я использую CFF Explorer), что все пакуется так, как мы и задумали.

Оригинальный файл:

Пишем упаковщик по шагам. Шаг второй. Пакуем.

Упакованный файл:

Пишем упаковщик по шагам. Шаг второй. Пакуем.

Как видно, Virtual Address + Virtual Size первой секции на второй картинке совпадает с SizeOfImage на первой. Виртуальный адрес первой секции также не изменился. Это именно то, чего мы и хотели добиться. На второй картинке также видно содержимое второй секции kaimi.ru. Степень сжатия неплоха - с 1266 кб до 362 кб.

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

И, как всегда, выкладываю последний вариант проекта с последними изменениями: own PE packer step 2

Также рекомендую почитать

Пишем упаковщик по шагам. Шаг второй. Пакуем. Обсудить на форуме


Источник: http://feedproxy.google.com/~r/kaimi/dev/~3/Q4WILS9t6T8/

Пишем упаковщик по шагам. Шаг второй. Пакуем. | | 2012-09-16 15:19:00 | | Блоги и всяко-разно | | Предыдущий шаг здесьСразу скажу, что по мере написания этого цикла статей я кое-что правлю и дорабатываю в своей библиотеке для работы с PE-файлами. Поэтому вам стоит ее перекачать и пересобрать - сейчас уже есть версия 0.1.3.И мы продолжаем написание собствен | РэдЛайн, создание сайта, заказать сайт, разработка сайтов, реклама в Интернете, продвижение, маркетинговые исследования, дизайн студия, веб дизайн, раскрутка сайта, создать сайт компании, сделать сайт, создание сайтов, изготовление сайта, обслуживание сайтов, изготовление сайтов, заказать интернет сайт, создать сайт, изготовить сайт, разработка сайта, web студия, создание веб сайта, поддержка сайта, сайт на заказ, сопровождение сайта, дизайн сайта, сайт под ключ, заказ сайта, реклама сайта, хостинг, регистрация доменов, хабаровск, краснодар, москва, комсомольск |
 
Дайджест новых статей по интернет-маркетингу на ваш email
Подписаться

Продающие сайты "под ключ"!

Наши сайты зарабытывают вам деньги. Landing-page. Эффективные продающие сайты точно в срок и под ключ! Всего от 14700 рублей
Подробнее...

Интернет-магазины и каталоги "под ключ"!

Эффективные и удобные инструменты торговли (электронной торговли) "под ключ". Продают, даже когда вы спите! Всего от 33800 рублей
Подробнее...

Комплексный интернет-маркетинг и продвижение сайтов

Максимальную эффективность дает не какой-то конкретный метод, а их комбинация. Комбинация таких методов и называется комплексным интернет-маркетингом. Всего от 8000 рублей в месяц
Подробнее...

Реклама в Yandex и Google

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

Social media marketing (SMM) — продвижение в социальных медиа

Реклама в Однокласcниках и на Mail.ru Создание, ведение и раскрутка групп и реклама ВКонтакте и Facebook. Всего от 8000 рублей в месяц
Подробнее...

Приглашаем к сотрудничеству рекламные агентства и веб-студии!

Внимание Акция! Приглашаем к сотрудничеству рекламные агентства и различные веб-студии России! Индивидуальные и взаимовыгодные условия сотрудничества.
Подробнее...

Ускоренная разработка любого сайта от 5 дней!

Внимание Акция! Ускоренная разработка любого сайта! Ваш сайт будет готов за 5-10 дней. Вы можете заказать разработку любого сайта "под ключ" за 5-10 рабочих дней, с доплатой всего 30% от его стоимости!
Подробнее...

Ждем новых друзей!

Внимание Акция! Ждем новых друзей! Скидка 10% на услуги по созданию и(или) обслуживанию вашего сайта при переходе к нам от другого разработчика.
Подробнее...

Приведи друга и получи скидку!

Внимание Акция! Приведи друга и получи скидку! Скидка 10% на услуги по созданию и(или) обслуживанию вашего сайта, если клиент заказавший наши услуги, пришел по Вашей рекомендации.
Подробнее...

1 2 3 4 5 6 7 8 9

Новые статьи и публикации



Мы создаем сайты, которые работают! Профессионально обслуживаем и продвигаем их , а также по всей России и ближнему зарубежью с 2006 года!

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

Заявка
Позвоните или оставьте заявку на сайте.


Консультация
Обсуждаем что именно Вам нужно и помогаем определить как это лучше сделать!


Договор
Заключаем договор на оказание услуг, в котором прописаны условия и обязанности обеих сторон.


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


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

Остались еще вопросы? Просто позвоните и задайте их специалистам
с 2:30 до 11:30 по Мск, звонок бесплатный
Или напишите нам в WhatsApp
с 9:30 до 18:30 по Хабаровску
Или напишите нам в WhatsApp
Веб-студия и агентство комплексного интернет-маркетинга «РЭДЛАЙН» © 2006 - 2024

Профессиональная Веб-разработка. Создание сайтов и магазинов "под ключ" , а также по всей России и зарубежью. Продвижение и реклама. Веб-дизайн. Приложения. Сопровождение. Модернизация. Интеграции. Консалтинг. Продвижение и реклама. Комплексный Интернет-маркетинг.

Оставьте заявку / Задайте вопрос

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Заказать услугу

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Обратный звонок

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Подписка на дайджест новостей

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

Заказать услуги со скидкой \ Бесплатная консультация







КАКИЕ УСЛУГИ ВАС ИНТЕРЕСУЮТ?

КАКИЕ ДОПОЛНИТЕЛЬНЫЕ УСЛУГИ ПОТРЕБУЮТСЯ?

Нажимая на кнопку ОТПРАВИТЬ, я даю согласие на обработку персональных данных
×

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

Что нужно сделать, чтобы заказать создание сайта у нас?

Ну для начала вам нужно представлять (хотя бы в общих чертах), что вы хотите получить от сайта и возможно каким вы хотите его видеть. А дальше все просто. Позвоните нам или оставьте заявку нашим менеджерам, чтобы они связались с Вами, проконсультировали и помогли определиться с подходящим именно Вам сайтом по цене, сроку, дизайну или функционалу. Если вы все ещё не уверены, какой сайт вам нужен, просто обратитесь к нам! Мы вместе проанализируем вашу ситуацию и определим максимально эффективный для вас вариант.

Быстрый заказ \ Консультация

Для всех тарифных планов на создание и размещение сайтов включено:

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

Комплексная раскрутка работает в рамках стратегии развития вашего бизнеса в сети и направлена

Быстрый заказ \ Консультация

ЭФФЕКТИВНОЕ СОПРОВОЖДЕНИЕ (ПОДДЕРЖКА, ОБСЛУЖИВАНИЕ) САЙТОВ

Полный комплекс услуг по сопровождению сайтаМы оказываем полный комплекс услуг по сопровождению сайта: информационному и техническому обслуживанию и развитию Интернет сайтов.

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

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

Быстрый заказ \ Консультация

Редизайн сайта и Адаптивный веб дизайн

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

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

Адаптивный дизайн сайтов и веб-приложений

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

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

Быстрый заказ \ Консультация

Контекстная реклама в Яндекс и GoogleКонтекстная реклама - это эффективный инструмент в интернет маркетинге, целью которого является увеличение продаж. Главный плюс контекстной рекламы заключается в том, что она работает избирательно.

Реклама в поисковых системах Яндекс и Google. Профессиональная настройка рекламы и отслеживание эффективности!

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

Быстрый заказ \ Консультация

Скидка

1500 руб.
Заинтересовались услугами создания, обслуживания или продвижения вашей компании в Интернете?!
Получите 1500 руб.
за он-лайн заявку
Предложение ограничено.

После получения заявки с Вами свяжутся наши специалисты и уточнят все детали по интересующей вас услуге.
«Нажимая на кнопку "Получить скидку", я даю согласие на обработку персональных данных»
×
Получите 1500 рублей!
×
×