Предыдущий шаг здесь.
Идем дальше! Пришло время написать распаковщик, именно этим мы начнем заниматься в этом шаге. Обрабатывать исходную таблицу импорта мы пока не будем, так как и в этом уроке нам будет, чем заняться.
Начнем мы вот с чего. Для работы распаковщика нам стопроцентно потребуются две WinAPI-функции: LoadLibraryA и GetProcAddress. В своем старом упаковщике я писал стаб распаковщика на MASM32 и вообще не создавал таблицу импорта. Я искал адреса этих функций в ядре, что несколько сложно и хардкорно, кроме того, это может вызвать неиллюзорные подозрения у антивирусов. Давайте в этот раз создадим обычную таблицу импортов и сделаем так, чтобы загрузчик сам нам сообщил адреса этих функций! Разумеется, набор из двух этих функций в таблице импорта так же подозрителен, как и полное их отсутствие, но ничто нам не мешает в будущем добавить еще другие левые случайные импорты из различных DLL-файлов. Куда загрузчик будет записывать адреса этих двух функций? Пора расширить нашу структуру packed_file_info!
1 2 3 4 5 6 7 8 9 10 11 |
//Структура, хранящая информацию об упакованном файле 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. (На старую оригинальную мы пока что наплюем).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//.... //Устанавливаем для нее необходимый виртуальный размер 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, его содержимое будет таким:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#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, в свою очередь, подключим этот файл:
1 2 |
//Заголовочный файл с нашими структурами #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 в инклюды проекта упаковщика - в этот файл мы будем вписывать необходимые параметры для сборки распаковщика:
1 2 |
//Заголовочный файл с параметрами распаковщика #include "../unpacker/parameters.h" |
Теперь начнем писать сам распаковщик. Открываем файл unpacker.cpp...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//Подключаем файл со структурами из проекта упаковщика #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!".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//Адрес загрузки образа unsigned int original_image_base; //Относительный адрес первой секции, //в которую упаковщик кладет информацию для //распаковщика и сами упакованные данные unsigned int rva_of_first_section; //Эти инструкции нужны только для того, чтобы //заменить в билдере распаковщика адреса на реальные __asm { mov original_image_base, 0x11111111; mov rva_of_first_section, 0x22222222; } |
Здесь мы объявили две локальные переменные. Первая будет содержать действительный адрес загрузки образа, а вторая - относительный адрес самой первой секции, в которую, как вы помните, мы кладем всю необходимую для распаковщика информацию и сами упакованные данные. Вместо чисел 0x11111111 и 0x22222222 мы с помощью упаковщика будем записывать реальные значения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Получаем указатель на структуру с информацией, //которую для нас заботливо приготовил упаковщик const packed_file_info* info; //Она находится в самом начале //первой секции упакованного файла info = reinterpret_cast<const packed_file_info*>(original_image_base + rva_of_first_section); //Два тайпдефа прототипов функций LoadLibraryA и GetProcAddress typedef HMODULE (__stdcall* load_library_a_func)(const char* library_name); typedef INT_PTR (__stdcall* get_proc_address_func)(HMODULE dll, const char* 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-функциях нельзя одновременно объявить переменную и сразу же присвоить ей значение.
И последняя (пока что) часть кода распаковщика:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//Создаем буфер на стеке 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); //Тайпдеф прототипа функции MessageBoxA typedef int (__stdcall* message_box_a_func)(HWND owner, const char* text, const char* 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 и начнем писать код.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#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; return 0; } //Открываем файл 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[] = {"; //Текущая длина считанных данных unsigned long 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<unsigned long>(static_cast<unsigned char>(*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; } return 0; } |
Не буду детально описывать этот код - многое вам уже будет знакомо. Скажу только, что он просто формирует из файла unpacker.exe файл unpacker.h вида:
1 2 3 4 5 6 7 8 9 10 |
#pragma once unsigned char 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):
1 |
"..\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):
1 |
copy /Y "..\$(Configuration)\unpacker_converter.exe" "..\unpacker_converter.exe" |
Осталось расставить зависимости (Project Dependencies): unpacker от unpacker_converter (вохможно, это не совсем логично, ну да ладно). После этого у нас все будет собираться и в Release, и в Debug-конфигурации.
Поясню, что мы запишем в файл parameters.h. Его содержимое будет таким:
1 2 3 4 |
#pragma once static const unsigned int original_image_base_offset = 0x0C; static const unsigned int rva_of_first_section_offset = 0x13; |
Мы пишем смещения относительно начала кода распаковщика (в собранном бинарном виде) двух чисел - 0x11111111 и 0x22222222. Эти числа будут перезаписываться упаковщиком, а смещения 0xC (12) и 0x13 (19) просчитываются в любом HEX-редакторе или с помощью автогенеренного файла unpacker.h. Меняться они уже вряд ли будут, так как код перед двумя ассемблерными командами mov в распаковщике мы больше дописывать не будем.
Добавим в include проекта simple_pe_packer автогенеренный файл unpacker.h:
1 2 |
//Тело распаковщика (автогенеренное) #include "unpacker.h" |
Завершающим этапом урока будет вставка тела распаковщика в упаковываемый файл. В прошлом шаге мы делали так:
1 2 |
//В будущем тут будет код распаковщика и что-то еще unpacker_section.get_raw_data() = "Nothing interesting here..."; |
Теперь будем вставлять туда код распаковщика и настраивать его:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//... { //Получаем ссылку на данные секции распаковщика std::string& unpacker_section_data = unpacker_section.get_raw_data(); //Записываем туда код распаковщика //Этот код хранится в автогенеренном файле //unpacker.h, который мы подключили в main.cpp unpacker_section_data = std::string(reinterpret_cast<const char*>(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
Слушай признайся, как тебя прет на вот это все, это же время нужно, откуда у тебя столько времени?)
Когда интересно чем-то заниматься, время всегда найдется :)
Спасибо за обзор. Почему написал, что винда читает иконку по секции .rsrc? Ведь можно вообще затереть имена и от этого иконка не исчезнет.
Во втором шаге комментарий по этому поводу, я же ответил уже.
Спасибо большое.
dx, проблемы при сборке unpacker_converter:
1>------ Построение начато: проект: unpacker_converter, Конфигурация: Debug Win32 ------
1>Построение начато 25.09.2013 19:40:36.
1>InitializeBuildStatus:
1> Обращение к "Debug\unpacker_converter.unsuccessfulbuild".
1>ClCompile:
1> Для всех выходных данных обновления не требуется.
1>ManifestResourceCompile:
1> Для всех выходных данных обновления не требуется.
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: void __thiscall std::basic_ios<char,struct std::char_traits >::setstate(int,bool)" (?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: class std::basic_streambuf<char,struct std::char_traits > * __thiscall std::basic_ios<char,struct std::char_traits >::rdbuf(void)const " (?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: void __thiscall std::_Container_base12::_Orphan_all(void)" (?_Orphan_all@_Container_base12@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: __thiscall std::_Container_base12::_Container_base12(void)" (??0_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: __thiscall std::_Container_base12::~_Container_base12(void)" (??1_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: class std::locale::facet * __thiscall std::locale::facet::_Decref(void)" (?_Decref@facet@locale@std@@QAEPAV123@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: bool __thiscall std::ios_base::good(void)const " (?good@ios_base@std@@QBE_NXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: int __thiscall std::ios_base::flags(void)const " (?flags@ios_base@std@@QBEHXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: void __thiscall std::locale::facet::_Incref(void)" (?_Incref@facet@locale@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: class std::basic_ostream<char,struct std::char_traits > & __thiscall std::basic_ostream<char,struct std::char_traits >::flush(void)" (?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: int __thiscall std::basic_streambuf<char,struct std::char_traits >::sputc(char)" (?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: __int64 __thiscall std::basic_streambuf<char,struct std::char_traits >::sputn(char const *,__int64)" (?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPBD_J@Z) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "protected: char * __thiscall std::basic_streambuf<char,struct std::char_traits >::gptr(void)const " (?gptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "protected: char * __thiscall std::basic_streambuf<char,struct std::char_traits >::_Gninc(void)" (?_Gninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: void __thiscall std::basic_ostream<char,struct std::char_traits >::_Osfx(void)" (?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: class std::basic_ostream<char,struct std::char_traits > * __thiscall std::basic_ios<char,struct std::char_traits >::tie(void)const " (?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "protected: char * __thiscall std::basic_streambuf<char,struct std::char_traits >::_Pninc(void)" (?_Pninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_base.obj) : error LNK2005: "public: __thiscall std::locale::id::operator unsigned int(void)" (??Bid@locale@std@@QAEIXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_32_64.obj) : error LNK2005: "public: __thiscall std::_Container_base12::_Container_base12(void)" (??0_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_32_64.obj) : error LNK2005: "public: __thiscall std::_Container_base12::~_Container_base12(void)" (??1_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_32_64.obj) : error LNK2005: "public: void __thiscall std::_Container_base12::_Orphan_all(void)" (?_Orphan_all@_Container_base12@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_32_64.obj) : error LNK2005: "public: void __thiscall std::basic_ios<char,struct std::char_traits >::setstate(int,bool)" (?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>pe_lib.lib(pe_32_64.obj) : error LNK2005: "public: class std::basic_streambuf<char,struct std::char_traits > * __thiscall std::basic_ios<char,struct std::char_traits >::rdbuf(void)const " (?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(syserror.obj) : error LNK2005: "public: __thiscall std::_Container_base12::_Container_base12(void)" (??0_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(syserror.obj) : error LNK2005: "public: __thiscall std::_Container_base12::~_Container_base12(void)" (??1_Container_base12@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(syserror.obj) : error LNK2005: "public: void __thiscall std::_Container_base12::_Orphan_all(void)" (?_Orphan_all@_Container_base12@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(ios.obj) : error LNK2005: "public: class std::locale::facet * __thiscall std::locale::facet::_Decref(void)" (?_Decref@facet@locale@std@@QAEPAV123@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(locale0.obj) : error LNK2005: "private: static void __cdecl std::locale::facet::_Facet_Register(class std::locale::facet *)" (?_Facet_Register@facet@locale@std@@CAXPAV123@@Z) уже определен в msvcprtd.lib(locale0_implib.obj)
1>libcpmtd.lib(locale0.obj) : error LNK2005: "public: class std::locale::facet * __thiscall std::locale::facet::_Decref(void)" (?_Decref@facet@locale@std@@QAEPAV123@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(locale0.obj) : error LNK2005: "private: static class std::locale::_Locimp * __cdecl std::locale::_Getgloballocale(void)" (?_Getgloballocale@locale@std@@CAPAV_Locimp@12@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(locale0.obj) : error LNK2005: "public: void __thiscall std::locale::facet::_Incref(void)" (?_Incref@facet@locale@std@@QAEXXZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(xlock.obj) : error LNK2005: "public: __thiscall std::_Lockit::_Lockit(int)" (??0_Lockit@std@@QAE@H@Z) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>libcpmtd.lib(xlock.obj) : error LNK2005: "public: __thiscall std::_Lockit::~_Lockit(void)" (??1_Lockit@std@@QAE@XZ) уже определен в msvcprtd.lib(MSVCP100D.dll)
1>LIBCMTD.lib(setlocal.obj) : error LNK2005: __configthreadlocale уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __free_dbg уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbghook.obj) : error LNK2005: __crt_debugger_hook уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(invarg.obj) : error LNK2005: __invalid_parameter уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(invarg.obj) : error LNK2005: __invoke_watson уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(mlock.obj) : error LNK2005: __lock уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: _exit уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __exit уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __cexit уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __amsg_exit уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0dat.obj) : error LNK2005: __initterm_e уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(winxfltr.obj) : error LNK2005: __XcptFilter уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0.obj) : error LNK2005: _mainCRTStartup уже определен в MSVCRTD.lib(crtexe.obj)
1>LIBCMTD.lib(errmode.obj) : error LNK2005: ___set_app_type уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_a уже определен в MSVCRTD.lib(cinitexe.obj)
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xi_z уже определен в MSVCRTD.lib(cinitexe.obj)
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_a уже определен в MSVCRTD.lib(cinitexe.obj)
1>LIBCMTD.lib(crt0init.obj) : error LNK2005: ___xc_z уже определен в MSVCRTD.lib(cinitexe.obj)
1>LIBCMTD.lib(hooks.obj) : error LNK2005: "void __cdecl terminate(void)" (?terminate@@YAXXZ) уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbgrptw.obj) : error LNK2005: __CrtDbgReportW уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(_file.obj) : error LNK2005: __lock_file уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(_file.obj) : error LNK2005: __unlock_file уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(fflush.obj) : error LNK2005: _fflush уже определен в MSVCRTD.lib(MSVCR100D.dll)
1>LINK : warning LNK4098: библиотека по умолчанию "MSVCRTD" противоречит использованию других библиотек; используйте параметр /NODEFAULTLIB:library
1>LINK : warning LNK4098: библиотека по умолчанию "LIBCMTD" противоречит использованию других библиотек; используйте параметр /NODEFAULTLIB:library
1>C:\Users\user1\Documents\Visual Studio 2010\Projects\portable_executable_library\simple_pe_packer\simple_pe_packer\Debug\unpacker_converter.exe : fatal error LNK1169: обнаружен многократно определенный символ - один или более
1>
1>СБОЙ построения.
1>
1>Затраченное время: 00:00:03.26
========== Построение: успешно: 0, с ошибками: 1, без изменений: 0, пропущено: 0 ==========
Что делать?
Видимо, какой-то из проектов солюшена собирается со статическими рантаймами, а какой-то с динамическими. Проблемно будет что-то собрать, не зная, как пользоваться компилятором... Проще скачать собранное.
А можно по подробнее?
Спасибо! Удалось исправить самому.
Как можно исправить это:
1> All outputs are up-to-date.
1> unpacker.vcxproj -> D:\Главное\SandBox\C++\Lib\Joni\Release\unpacker.exe
1>PostBuildEvent:
1> ""..\unpacker_converter.exe"" не является внутренней или внешней
1> командой, исполняемой программой или пакетным файлом.
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: The command ""..\unpacker_converter.exe" "..\Release\unpacker.exe" "..\Joni\unpacker.h"
1>C:\Program Files\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppCommon.targets(113,5): error MSB3073: :VCEnd" exited with code 9009.
1>
1>Build FAILED.
Здесь что-то с PostBuild-событием (работа, которая выполняется после сборки проекта). Надо посмотреть, где лежит файл unpacker_converter.exe и собрался ли он вообще. Если собрался, то убедиться, что в PostBuildEvent в Visual Studio указан корректный путь к нему и к аргументам командной строки, которые ему передаются.