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


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

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

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

Начнем мы вот с чего. Для работы распаковщика нам стопроцентно потребуются две WinAPI-функции: LoadLibraryA и GetProcAddress. В своем старом упаковщике я писал стаб распаковщика на MASM32 и вообще не создавал таблицу импорта. Я искал адреса этих функций в ядре, что несколько сложно и хардкорно, кроме того, это может вызвать неиллюзорные подозрения у антивирусов. Давайте в этот раз создадим обычную таблицу импортов и сделаем так, чтобы загрузчик сам нам сообщил адреса этих функций! Разумеется, набор из двух этих функций в таблице импорта так же подозрителен, как и полное их отсутствие, но ничто нам не мешает в будущем добавить еще другие левые случайные импорты из различных DLL-файлов. Куда загрузчик будет записывать адреса этих двух функций? Пора расширить нашу структуру packed_file_info!

//Структура, хранящая информацию об упакованном файлеstruct packed_file_info
{
  BYTE number_of_sections;//Количество секций в оригинальном файле
  DWORD size_of_packed_data;//Размер упакованных данных
  DWORD size_of_unpacked_data;//Размер оригинальных данных
 
  DWORD load_library_a;//Адрес процедуры LoadLibraryA из kernel32.dll
  DWORD get_proc_address;//Адрес процедуры GetProcAddress из kernel32.dll
  DWORD end_of_import_address_table;//Конец IAT};

Я добавил в структуру три поля. В первые два загрузчик впишет адреса функций LoadLibraryA и GetProcAddress из kernel32.dll. Последнее поле указывает на конец адресной таблицы импорта (import address table, IAT), и в него мы запишем ноль, чтобы дать понять загрузчику, что больше никаких функций нам не надо. Про это я еще расскажу немного дальше.

Теперь необходимо создать новую таблицу импорта. В этом нам сильно поможет моя библиотека для работы с PE. (На старую оригинальную мы пока что наплюем).

//....//Устанавливаем для нее необходимый виртуальный размер
      image.set_section_virtual_size(added_section, total_virtual_size);//....
 
      std::cout<<"Creating imports..."<< std::endl;
 
      //Создаем импорты из библиотеки kernel32.dll
      pe_base::import_library kernel32;
      kernel32.set_name("kernel32.dll");//Выставили имя библиотеки
 
      //Создаем импортируемую функцию
      pe_base::imported_function func;
      func.set_name("LoadLibraryA");//Ее имя
      kernel32.add_import(func);//Добавляем ее к библиотеке
 
      //И вторую функцию
      func.set_name("GetProcAddress");
      kernel32.add_import(func);//Тоже добавляем
 
      //Получаем относительный адрес (RVA) поля load_library_a//нашей структуры packed_file_info, которую мы расположили в самом//начале добавленной секции, помните?
      DWORD load_library_address_rva = pe_base::rva_from_section_offset(added_section,
        offsetof(packed_file_info, load_library_a));
 
      //Устанавливаем этот адрес как адрес//таблицы адресов импорта (import address table)
      kernel32.set_rva_to_iat(load_library_address_rva);
 
      //Создаем список импортируемых библиотек
      pe_base::imported_functions_list imports;//Добавляем к списку нашу библиотеку
      imports.push_back(kernel32);
 
      //Настроим пересборщик импортов
      pe_base::import_rebuilder_settings settings;//Original import address table нам не нужна (пояснения ниже)
      settings.build_original_iat(false);//Будем переписывать IAT именно по тому адресу,//который указали (load_library_address_rva)
      settings.save_iat_and_original_iat_rvas(true, true);//Расположим импорты прямо за концом упакованных данных
      settings.set_offset_from_section_start(added_section.get_raw_data().size());//Пересоберем импорты
      image.rebuild_imports(imports, added_section, settings);

Начало кода понятно - создали импорт библиотеки, добавили к ней пару функций, создали список импортируемых библиотек из одной-единственной kernel32.dll. Поясню строку, где мы устанавливаем RVA к IAT (kernel32.set_rva_to_iat). Я уже раньше писал кое-что об импортах PE-файла. Расскажу вкратце еще разок. Для каждой импортируемой библиотеки в таблице импортов создается следующая структура:

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

Загрузчик записывает адреса импортируемых функций в Import Address Table (IAT) для каждой импортируемой DLL, а имена или ординалы импортируемых функций он берет из Original Import Address Table (или, по-другому, Import Lookup Table). Можно обойтись и без последней, например, все компиляторы Borland всегда так делают, плевать они хотели на Import Lookup Table. В этом случае у нас в единственной таблице Import Address Table содержатся сразу ординалы или имена импортируемых функций, и туда же, поверх этих данных, загрузчик запишет адреса непосредственно импортированных функций. Мы тоже не будем делать Original Import Address Table, обойдемся без нее (меньше места импорт займет), поэтому отключаем эту опцию в пересборщике импортов.

Вызов settings.save_iat_and_original_iat_rvas настраивает пересборщик таким образом, что он не будет создавать свои собственные IAT и Original IAT, а запишет все по тем адресам, которые уже указаны в каждой библиотеке (помните вызов kernel32.set_rva_to_iat?).

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

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

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

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

Как видно, по адресам 0x1009 и 0x100D записались именно те адреса, которые нам нужны, значит, все сделано правильно. (Адрес точки входа пока что совершенно левый, и нет никакого распаковщика, поэтому файл по-прежнему не запустится, но мы уже достигли многого).

Идем дальше. Необходимо подготовить наши сорсы для написания распаковщика. Вынесем все структуры из файла main.cpp в файл structs.h, его содержимое будет таким:

#pragma once#include <Windows.h>#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;//Характеристики секции};
 
//Структура, хранящая информацию об упакованном файлеstruct packed_file_info
{
  BYTE number_of_sections;//Количество секций в оригинальном файле
  DWORD size_of_packed_data;//Размер упакованных данных
  DWORD size_of_unpacked_data;//Размер оригинальных данных
 
  DWORD load_library_a;//Адрес процедуры LoadLibraryA из kernel32.dll
  DWORD get_proc_address;//Адрес процедуры GetProcAddress из kernel32.dll
  DWORD end_of_import_address_table;//Конец IAT};#pragma pack(pop)

Тут пояснять ничего не нужно, мы просто перенесли код. В main.cpp, в свою очередь, подключим этот файл:

//Заголовочный файл с нашими структурами#include "structs.h"

И наступает время хардкора! Будем писать распаковщик. Немного поразмыслив, я решил не использовать MASM32, а писать его на C с элементами C++ и ассемблерными вставками - читаемость кода будет выше. Итак, создаем новый проект в солюшене и называем его unpacker. Добавляем к нему файлы unpacker.cpp и parameters.h (создаем). Далее в настройках выставляем всё то же самое, что мы делали с проектом lzo-2.06 в самом первом шаге, чтобы сборка была самой минимальной по размеру и базонезависимой. Точку входа (Linker - Advanced - Entry Point) выставляем в unpacker_main. Далее, в Configuration Manager'е (см. шаг 1) выставляем, чтобы этот проект всегда собирался в конфигурации Release:

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

Проставим у проекта simple_pe_packer зависимость от проекта unpacker (Project Dependencies, как в шаге 1) и добавим файл parameters.h в инклюды проекта упаковщика - в этот файл мы будем вписывать необходимые параметры для сборки распаковщика:

//Заголовочный файл с параметрами распаковщика#include "../unpacker/parameters.h"

Теперь начнем писать сам распаковщик. Открываем файл unpacker.cpp...

//Подключаем файл со структурами из проекта упаковщика#include "../simple_pe_packer/structs.h"//Создадим функцию без пролога и эпилогаvoid __declspec(naked) unpacker_main(){//Пролог вручную
  __asm
  {
    push ebp;
    mov ebp, esp;
    sub esp, 128;}
 
  //... описано далее ...//
 
  //Эпилог вручную
  _asm
  {
    leave;
    ret;}}

Итак, начинаю разъяснения. Сначала мы подключили файл, содержащий объявления структур упаковщика - в распаковщике они нам пригодятся. Далее мы создаем точку входа - процедуру unpacker_main. Обратите внимание, что это особо объявленная функция - naked. Это говорит компилятору о том, что не нужно создавать для этой функции пролог и эпилог (стековый фрейм) автоматически. Нам это необходимо сделать вручную, а зачем - поясню в следующем уроке. Пока что мы создаем точь-в-точь такие же пролог и эпилог, которые делает сам компилятор MSVC++. Строка "sub esp, 128" выделяет на стеке 128 байтов - этого нам пока должно хватить для подручных нужд. В этом шаге распаковщик не будет делать чего-то серьезного. Пролог и эпилог нужны нам, чтобы мы могли выделять память на стеке без лишних проблем. В самом конце мы пишем инструкцию ret - возврат в ядро. Теперь напишем самое простое тело упаковщика. Пусть он будет просто приветствовать нас, выдавая Message Box с текстом "Hello!".

//Адрес загрузки образаunsignedint original_image_base;//Относительный адрес первой секции,//в которую упаковщик кладет информацию для//распаковщика и сами упакованные данныеunsignedint rva_of_first_section;
 
  //Эти инструкции нужны только для того, чтобы//заменить в билдере распаковщика адреса на реальные
  __asm
  {
    mov original_image_base, 0x11111111;
    mov rva_of_first_section, 0x22222222;}

Здесь мы объявили две локальные переменные. Первая будет содержать действительный адрес загрузки образа, а вторая - относительный адрес самой первой секции, в которую, как вы помните, мы кладем всю необходимую для распаковщика информацию и сами упакованные данные. Вместо чисел 0x11111111 и 0x22222222 мы с помощью упаковщика будем записывать реальные значения.

//Получаем указатель на структуру с информацией,//которую для нас заботливо приготовил упаковщикconst packed_file_info* info;//Она находится в самом начале//первой секции упакованного файла
  info =reinterpret_cast<const packed_file_info*>(original_image_base + rva_of_first_section);
 
  //Два тайпдефа прототипов функций LoadLibraryA и GetProcAddresstypedef HMODULE (__stdcall* load_library_a_func)(constchar* library_name);typedef INT_PTR (__stdcall* get_proc_address_func)(HMODULE dll, constchar* func_name);
 
  //Считаем их адреса из структуры packed_file_info//Их нам туда подложил загрузчик
  load_library_a_func load_library_a;
  get_proc_address_func get_proc_address;
  load_library_a =reinterpret_cast<load_library_a_func>(info->load_library_a);
  get_proc_address =reinterpret_cast<get_proc_address_func>(info->get_proc_address);

Здесь, кажется, все понятно. В начале первой секции упакованного файла лежит структура packed_file_info, которую мы создаем в упаковщике. В ней есть еще три поля, заполняемые самим загрузчиком - мы так устроили таблицу импортов, как вы помните. Из этих полей мы получаем адреса функций LoadLibraryA и GetProcAddress. Вы еще можете спросить, зачем я сначала объявляю все переменные, и только позже присваиваю им значения, ведь я мог бы это делать одной строкой. Все дело в том, что в naked-функциях нельзя одновременно объявить переменную и сразу же присвоить ей значение.

И последняя (пока что) часть кода распаковщика:

//Создаем буфер на стекеchar buf[32];//user32.dll*reinterpret_cast<DWORD*>(&buf[0])='resu';*reinterpret_cast<DWORD*>(&buf[4])='d.23';*reinterpret_cast<DWORD*>(&buf[8])='ll';
 
  //Загружаем библиотеку user32.dll
  HMODULE user32_dll;
  user32_dll = load_library_a(buf);
 
  //Тайпдеф прототипа функции MessageBoxAtypedefint(__stdcall* message_box_a_func)(HWND owner, constchar* text, constchar* caption, DWORD type);
 
  //MessageBoxA*reinterpret_cast<DWORD*>(&buf[0])='sseM';*reinterpret_cast<DWORD*>(&buf[4])='Bega';*reinterpret_cast<DWORD*>(&buf[8])='Axo';
 
  //Получаем адрес функции MessageBoxA
  message_box_a_func message_box_a;
  message_box_a =reinterpret_cast<message_box_a_func>(get_proc_address(user32_dll, buf));
 
  //Hello!*reinterpret_cast<DWORD*>(&buf[0])='lleH';*reinterpret_cast<DWORD*>(&buf[4])='!!o';
 
  //Выводим месадж бокс
  message_box_a(0, buf, buf, MB_ICONINFORMATION);

Здесь в целом тоже все должно быть понятно, кроме странного заполнения строк. Мы выделили буфер buf на стеке. Строки все у нас также должны быть исключительно на стеке - мы ничего не можем писать в секцию данных, так как это неизбежно приведет к появлению релокаций, и код станет базозависимым. Именно поэтому мы так нелепо по 4 байта записываем строки непосредственно в стековый буфер. Нужно еще помнить про обратный порядок байтов, с которым работает архитектура x86, а мы именно под нее пишем код, поэтому буквы в кусках строк по 4 байта расположены в обратном порядке.

Сначала мы загружаем библиотеку user32.dll, затем получаем из нее адрес процедуры MessageBoxA, а затем вызываем ее. Вот и всё с распаковщиком!

Но осталась еще одна вещь - нам надо код распаковщика каким-то образом вставить в упакованный файл и настроить. Я решил это дело автоматизировать. Для этого добавим новый проект с именем unpacker_converter в солюшен. Цель этого проекта такова: он будет открывать получающийся после компиляции распаковщика файл unpacker.exe, считывать данные из его единственной секции (по сути, код) и преобразовывать его в h-файл, который мы заинклюдим в проекте simple_pe_packer. Пропишем в проекте unpacker_converter include-директорию как в проекте simple_pe_packer, чтобы можно было подключать h-файлы библиотеки для работы с PE-файлами, добавим в проект файл main.cpp и начнем писать код.

#include <iostream>#include <fstream>#include <vector>#include <string>#include <iomanip>//Заголовочный файл библиотеки для работы с PE-файлами#include <pe_32_64.h>
 
//Директивы для линкования с собранной библиотекой PE#ifndef _M_X64#ifdef _DEBUG#pragma comment(lib, "../../Debug/pe_lib.lib")#else#pragma comment(lib, "../../Release/pe_lib.lib")#endif#else#ifdef _DEBUG#pragma comment(lib, "../../x64/Debug/pe_lib.lib")#else#pragma comment(lib, "../../x64/Release/pe_lib.lib")#endif#endif
 
int main(int argc, char* argv[]){//Подсказка по использованиюif(argc !=3){
    std::cout<<"Usage: unpacker_converter.exe unpacker.exe output.h"<< std::endl;return0;}
 
  //Открываем файл unpacker.exe - его имя//и путь к нему хранятся в массиве argv по индексу 1
  std::ifstream file(argv[1], std::ios::in| std::ios::binary);if(!file){//Если открыть файл не удалось - сообщим и выйдем с ошибкой
    std::cout<<"Cannot open "<< argv[1]<< std::endl;return-1;}
 
  try{
    std::cout<<"Creating unpacker source file..."<< std::endl;
 
    //Пытаемся открыть файл как 32-битный PE-файл//Последние два аргумента false, потому что нам не нужны//"сырые" данные привязанных импортов файла и //"сырые" данные отладочной информации//При упаковке они не используются, поэтому не загружаем эти данные
    pe32 image(file, false, false);
 
    //Получаем список секций распаковщика
    pe_base::section_list& unpacker_sections = image.get_image_sections();//Убедимся, что она одна (так как в нем нет импортов, релокаций)if(unpacker_sections.size()!=1){
      std::cout<<"Incorrect unpacker"<< std::endl;return-1;}
 
    //Получаем ссылку на данные этой секции
    std::string& unpacker_section_data = unpacker_sections.at(0).get_raw_data();//Удаляем нулевые байты в конце этой секции,//которые компилятор добавил для выравнивания
    pe_base::strip_nullbytes(unpacker_section_data);
 
    //Открываем выходной файл для записи h-файла//Его имя хранится в argv[2]
    std::ofstream output_source(argv[2], std::ios::out| std::ios::trunc);
 
    //Начинаем формировать исходный код
    output_source << std::hex<<"#pragma once"<< std::endl<<"unsigned char unpacker_data[] = {";//Текущая длина считанных данныхunsignedlong len =0;//Общая длина данных секции
    std::string::size_type total_len = unpacker_section_data.length();
 
    //Для каждого байта данных...for(std::string::const_iterator it = unpacker_section_data.begin(); it != unpacker_section_data.end();++it, ++len){//Добавляем необходимые переносы, чтобы//получившийся код был читаемымif((len %16)==0)
        output_source << std::endl;
 
      //Записываем значение байта
      output_source
        <<"0x"<< std::setw(2)<< std::setfill('0')<<static_cast<unsignedlong>(static_cast<unsignedchar>(*it));
 
      //И, если необходимо, запятуюif(len != total_len -1)
        output_source <<", ";}
 
    //Конец кода
    output_source <<" };"<< std::endl;}catch(const pe_exception& e){//Если по какой-то причине открыть его не удалось//Выведем текст ошибки и выйдем
    std::cout<< e.what()<< std::endl;return-1;}
 
  return0;}

Не буду детально описывать этот код - многое вам уже будет знакомо. Скажу только, что он просто формирует из файла unpacker.exe файл unpacker.h вида:

#pragma onceunsignedchar unpacker_data[]={0x55, 0x8b, 0xec, 0x81, 0xec, 0x80, 0x00, 0x00, 0x00, 0xc7, 0x45, 0xfc, 0x11, 0x11, 0x11, 0x11, 
0xc7, 0x45, 0xf8, 0x22, 0x22, 0x22, 0x22, 0x8b, 0x45, 0xfc, 0x03, 0x45, 0xf8, 0x8b, 0x48, 0x09, 
0x8b, 0x70, 0x0d, 0x8d, 0x45, 0xd8, 0x50, 0xc7, 0x45, 0xd8, 0x75, 0x73, 0x65, 0x72, 0xc7, 0x45, 
0xdc, 0x33, 0x32, 0x2e, 0x64, 0xc7, 0x45, 0xe0, 0x6c, 0x6c, 0x00, 0x00, 0xff, 0xd1, 0x8d, 0x4d, 
0xd8, 0x51, 0x50, 0xc7, 0x45, 0xd8, 0x4d, 0x65, 0x73, 0x73, 0xc7, 0x45, 0xdc, 0x61, 0x67, 0x65, 
0x42, 0xc7, 0x45, 0xe0, 0x6f, 0x78, 0x41, 0x00, 0xff, 0xd6, 0x6a, 0x40, 0x8d, 0x4d, 0xd8, 0x51, 
0x51, 0x6a, 0x00, 0xc7, 0x45, 0xd8, 0x48, 0x65, 0x6c, 0x6c, 0xc7, 0x45, 0xdc, 0x6f, 0x21, 0x00, 
0x00, 0xff, 0xd0, 0xc9, 0xc3};

Эти данные являются шестнадцатеричным представлением данных первой и единственной секции кода распаковщика. Он у нас пока совсем простой и маленький. Как же сделать, чтобы unpacker_converter автоматически генерировал для нас такой h-файл при пересборке распаковщика? Необходимо поправить настройку проекта unpacker (Build Events - Post-Build Event):

"..\unpacker_converter.exe" "..\Release\unpacker.exe" "..\simple_pe_packer\unpacker.h"

Почему я в этой настройке не использовал макрос $(Configuration)? Потому что он для проекта unpacker всегда будет раскрываться в "Release", так как и в дебаге, и в релизе этот проект собирается как Release (мы это меняли ранее в Configuration Manager'е). Поэтому мы просто будем копировать файл unpacker_converter.exe из ЕГО текущей конфигурации в корень проекта, и оттуда его уже сможет взять проект unpacker. Таким образом, последнее, что мы делаем, это правим конфигурацию проекта unpacker_converter (Build Events - Post-Build Event):

copy /Y "..\$(Configuration)\unpacker_converter.exe" "..\unpacker_converter.exe"

Осталось расставить зависимости (Project Dependencies): unpacker от unpacker_converter (вохможно, это не совсем логично, ну да ладно). После этого у нас все будет собираться и в Release, и в Debug-конфигурации.

Поясню, что мы запишем в файл parameters.h. Его содержимое будет таким:

#pragma once
 
staticconstunsignedint original_image_base_offset =0x0C;staticconstunsignedint rva_of_first_section_offset =0x13;

Мы пишем смещения относительно начала кода распаковщика (в собранном бинарном виде) двух чисел - 0x11111111 и 0x22222222. Эти числа будут перезаписываться упаковщиком, а смещения 0xC (12) и 0x13 (19) просчитываются в любом HEX-редакторе или с помощью автогенеренного файла unpacker.h. Меняться они уже вряд ли будут, так как код перед двумя ассемблерными командами mov в распаковщике мы больше дописывать не будем.

Добавим в include проекта simple_pe_packer автогенеренный файл unpacker.h:

//Тело распаковщика (автогенеренное)#include "unpacker.h"

Завершающим этапом урока будет вставка тела распаковщика в упаковываемый файл. В прошлом шаге мы делали так:

//В будущем тут будет код распаковщика и что-то еще
      unpacker_section.get_raw_data()="Nothing interesting here...";

Теперь будем вставлять туда код распаковщика и настраивать его:

//...{//Получаем ссылку на данные секции распаковщика
        std::string& unpacker_section_data = unpacker_section.get_raw_data();//Записываем туда код распаковщика//Этот код хранится в автогенеренном файле//unpacker.h, который мы подключили в main.cpp
        unpacker_section_data = std::string(reinterpret_cast<constchar*>(unpacker_data), sizeof(unpacker_data));//Записываем по нужным смещениям адрес//загрузки образа*reinterpret_cast<DWORD*>(&unpacker_section_data[original_image_base_offset])= image.get_image_base_32();//и виртуальный адрес самой первой секции упакованного файла,//в которой лежат данные для распаковки и информация о них//В самом начале это секции, как вы помните, лежит//структура packed_file_info*reinterpret_cast<DWORD*>(&unpacker_section_data[rva_of_first_section_offset])= image.get_image_sections().at(0).get_virtual_address();}
 
      //Добавляем и эту секциюconst pe_base::section& unpacker_added_section = image.add_section(unpacker_section);//Выставляем новую точку входа - теперь она указывает//на распаковщик, на самое его начало
      image.set_ep(image.rva_from_section_offset(unpacker_added_section, 0));//...

Всё! Теперь распаковщик будет настраиваться и добавляться в упакованный файл! Давайте убедимся в этом. Как всегда, упакуем сами себя, получив на выходе файл packed_simple_pe_packer.exe. Запустим его и увидим долгожданное окошко, ради которого было проделано столько работы!

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

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

Как всегда, прикладываю полный солюшен (кроме библиотеки для работы с PE-файлами) упаковщика: Own PE packer step 3

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

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


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

Пишем упаковщик по шагам. Шаг третий. Распаковываем. | | 2012-09-17 00:00:00 | | Блоги и всяко-разно | | Предыдущий шаг здесь.Идем дальше! Пришло время написать распаковщик, именно этим мы начнем заниматься в этом шаге. Обрабатывать исходную таблицу импорта мы пока не будем, так как и в этом уроке нам будет, чем заняться.Начнем мы вот с чего. Для работы распаковщ | РэдЛайн, создание сайта, заказать сайт, разработка сайтов, реклама в Интернете, продвижение, маркетинговые исследования, дизайн студия, веб дизайн, раскрутка сайта, создать сайт компании, сделать сайт, создание сайтов, изготовление сайта, обслуживание сайтов, изготовление сайтов, заказать интернет сайт, создать сайт, изготовить сайт, разработка сайта, 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 рублей!
×
×