Я новичок, изучаю понемногу формат PE-файлов, пишу простейший дампер PE-файлов. С "правильными" PE, созданными по стандарту, всё ОК, но тут я наткнулся на упакованный (вроде бы как upack'ом), и начались вопросы. Во-первых, DATA_DIRECTORY странным образом искажена: Адреса и размеры почти всех элементов DATA_DIRECTORY выходят далеко за пределы секций (в именах секций, кстати, тоже непонятно что): Код (Text): Section name: PSяХ«лзГ Section read: [executable] [readable] [writeable] [RVA: 00001000 RVA+RawSize: 000011F0, RAW address: 00000010] Section name: Section read: [executable] [readable] [writeable] [RVA: 00012000 RVA+RawSize: 00017404, RAW address: 00000200] Section name: FA Section read: [executable] [readable] [writeable] [RVA: 0001A000 RVA+RawSize: 0001A1F0, RAW address: 00000010] (а у первой и последней секций еще и адреса совпадают физические.) Единственная попадающая внутрь последней секции таблица импорта (в DATA_DIRECTORY элемент 1) тоже искажена: Тут вообще ни один из адресов не является правильным. Отсюда три вопроса: 1. Как вообще работать с таким exe? 2. Имеет ли он таблицу импорта? 2. Получается, системному загрузчику можно скармливать всё что угодно, и это будет загружаться и работать? Ссылка на сам exe: http://www.sendspace.com/file/hoxejc
1. Как и со всеми. 2. да 3. Нет, не все. Если вы не заметили, все, что имеет невалидные адреса не парсится загрузчиком, кроме фиксапов, но выставлен флаг, указывающий, что фиксапы отрезаны. Остальное вполне валидно. Про секции - один и тот же физический оффсет может отображатся на различные виртуальные адреса, но не наоборот. Имена пофик, они не важны (ну кроме ресурсов, но там отдельная песня) По импорту, скорее всего вы не правильно преобразовываете к оффсету. Дайте код.
1. upack не совсем тот пакер которые нужен для новичков ) PE заголовок там собран таким образом что оставлены нетронутыми только самые важные поля. Остальные поля являются частью кода. Как видно файловое смещение секции кода = 00000010h, а файловое выравнивание 00000200h, смещение меньше чем выравнивание, поэтому загрузчик проигнорирует это смещение и спроецирует в первую секцию данные начиная с начала файла. Точка входа (00001018h) минус виртуальное выравнивание (00001000h) = 0000018h. Получается по смещение 18h от начала файла начинается исполняемый код, также "являющийся" и данными PE заголовка ). Также обрати внимание на размер опционального заголовка. 2. Таблицу импорта он имеет 3. Не все что угодно, а только то что допустимо.
Мне тут подсказали вот такое: https://cracklab.ru/f/index.php?action=vthread&topic=4611&forum=1&page=-1 Здесь, видимо и есть это самое - RAW offset у первой и третьей секций меньше File Alignment, поэтому он выравнивается вниз и получается просто 0. Но как это обработать, я пока не могу понять. Код страшный, вот он: http://pastebin.com/EBK20hUJ CreateDump(const char * output, const char* input) - создает дамп PE файла по пути input и пишет лог в output. VirtualToPhysical - конвертирует rva в raw. Там GUI, но основные функции на него не опираются. Раньше был вариант не с чтением PE-файла от начала до конца и преобразованием RVA в RAW offset, а с загрузкой PE по секциям в память, как делает системный загрузчик, но он тоже с этим exe не справляется. ------ Flint_ta, спасибо, это я в теории понял (первая ссылка в этом посте об этом же вроде и говорит). Неплохо бы еще понять, как в таком файле найти таблицу импорта. Даже PE Tools не справляется.
GRRRLPower Совет: Рекомендую отображение значений в подсказывающих хинтах впредь включать в Hex виде, а то не очень-то удобно смотреть По одной простой причине, сис.программеру, тем более опытному быстрее сообразить на цифрах подобные : 0x401000, 0x78900000, etc чем в их десятичном. Ну и если он увидет 0xCCCCCCC или другое подобное дебаг-значение, то сразу же может тебе дать ответ )
Интересно, все-таки, прояснить вопрос по таблице импорта.. С учетом сказанного Flint_ta в #3 таблица импорта начинатся по адресу(в файле) 0х1ее, имеет размер 20 байт, т.е. запись об одном экспортируемом файле (kernel32.dll). Но поля этой структуры мало поддаются осмыслению: -Указатель на массив с адресами названий имп-х функций(1 копия) 0х0 -Указатель на строку с именем dll - 0х2 (ну это еще можно переварить..) -Указатель на массив с адресами названий имп-х функций(2 копия) 0х003511е8 Вот последнюю цифру не могу переварить - по идее должно быть 0х000001е8(0х00011е8)... Flint_ta - может объясните?
EvilsInterrupt, спасибо за совет, включил) Это вообще забавно, получается, что RVA Name может вести вообще мимо существующих секций. Интересно, для чего еще такие значения допустимы? Тоже не могу понять.
GRRRLPower 1) Истинный размер образа это ALIGN_UP(SizeOfImage, SectionAlignment), таким образом если в хидере "пофиксать", то AddressOfEntryPoint может быть больше чем SizeOfImage !!! 2) Истинное начало секции это ALIGN_DOWN( PointerToRawData, FileAlignment ) /// ALIGN_DOWN( VirtualAddress, SectionAlignment); 3) Размер секции весьма хитрое значение, его надо: VS = ALIGN( VirtualSize, SectionAlignment ) RS = ALIGN( SizeOfRawData , FileAlignment ) len = MIN(VS, RS) + учесть размер файла ну или образа 4) офсет первой секции есть: 4 + sizeof(image_file_header) + pe->fileheader.SizeOfRawData С импортом возиться надо тогда, когда все пункты выше оттестированы и п.3 особенно !!!
загруженная в память секция (остальная часть страницы памяти обнулена) обнулим байт для получения корректной таблицы импорта
EvilsInterrupt, благодарю за подробное пояснение всего, что стоит учесть, второй пункт я уже учел, а для четвертого есть макрос IMAGE_FIRST_SECTION. Насчет первого: Думаю, что в хедере это значение менять нельзя, оно должно быть строго равно виртуальному адресу последней секции плюс ее выровненный размер, иначе PE не загрузится. Насчет третьего не совсем понял, о каком размере идет речь - в памяти или в образе на диске. Этот вопрос хорошо Крис Касперски осветил: Bazhan - не совсем понятно, зачем обнулять байт по указанному адресу? Там же, по спецификации, уже должен лежать RVA? Секции перекрываться не могут в памяти, поясните пожалуйста, как там может оказаться ноль?
>>Думаю, что в хедере это значение менять нельзя, оно должно быть строго равно виртуальному адресу последней секции плюс ее выровненный размер, иначе PE не загрузится. Были прецеденты! Файл грузился, а размер не был выровнен Выравнивай!
Ну Bazhan объяснил: "загруженная в память секция (остальная часть страницы памяти обнулена)" Подробнее: оперирования с полями из таблицы импорта происходит после загрузки(проецирования) секций в память процесса. В нашем случае размер проецирования составляет 0x200 байт, а последующие байты (35 00..) спроецированы не будут и на их месте будут нули - и ... Указатель на массив с адресами названий имп-х функций(2 копия) будет 0х000011е8 Непонятно другое: Указатель на таблицу импорта равен 0х0001а1ее - значит она располагается в 3 секции, которая проецируется с адреса 0х0001а000(без базы) ... и по-идее должно быть: -Указатель на строку с именем dll - 0х0001а002 -Указатель на массив с адресами названий имп-х функций(2 копия) 0х001а11е8 но лежит -Указатель на строку с именем dll - 0х00000002 -Указатель на массив с адресами названий имп-х функций(2 копия) 0х000011е8 -вообще, загадка...
под отладчиком Код (Text): kd> !dh 400000 File Type: EXECUTABLE IMAGE FILE HEADER VALUES 14C machine (i386) 3 number of sections 4011B0BE time date stamp Sat Jan 24 01:39:42 2004 FF50AD00 file pointer to symbol table 7CEB3476 number of symbols 148 size of optional header 10F characteristics Relocations stripped Executable Line numbers stripped Symbols stripped 32 bit word machine OPTIONAL HEADER VALUES 10B magic # 76.111 linker version 694C6461 size of code 72617262 size of initialized data 4179 size of uninitialized data 1018 address of entry point 10 base of code ----- new ----- 00400000 image base 1000 section alignment 200 file alignment 2 subsystem (Windows GUI) 4.00 operating system version 0.57 image version 4.00 subsystem version 1B000 size of image 200 size of headers 0 checksum 00100000 size of stack reserve 00001000 size of stack commit 00100000 size of heap reserve 00001000 size of heap commit 0 DLL characteristics 0 [ 0] address [size] of Export Directory 1A1EE [ 14] address [size] of Import Directory 0 [ 0] address [size] of Resource Directory AD3876FF [BE3E8B50] address [size] of Exception Directory 41A0F0 [F359276A] address [size] of Security Directory 476FFA5 [8BFFC883] address [size] of Base Relocation Directory 1CEBABDF [ 0] address [size] of Debug Directory 50746547 [41636F72] address [size] of Description Directory 65726464 [ 7373] address [size] of Special Directory 0 [ 0] address [size] of Thread Storage Directory B140AB40 [C1ABF304] address [size] of Load Configuration Directory 7B50AE0 [7E8BABF3] address [size] of Bound Import Directory E951570C [ 161FF] address [size] of Import Address Table Directory E3E21056 [E0D304B1] address [size] of Delay Import Directory 538DE803 [55C03318] address [size] of COR20 Header Directory E0D35140 [FF91EA8B] address [size] of Reserved Directory SECTION HEADER #1 PSÿÕ«ëçà name 11000 virtual size 1000 virtual address 1F0 size of raw data 10 file pointer to raw data 412000 file pointer to relocation table 4172A7 file pointer to line numbers 3 number of relocations 0 number of line numbers E0000060 flags Code Initialized Data (no align specified) Execute Read Write SECTION HEADER #2 name 8000 virtual size 12000 virtual address 5404 size of raw data 200 file pointer to raw data 401000 file pointer to relocation table 410FFF file pointer to line numbers 7404 number of relocations 41 number of line numbers E0000060 flags Code Initialized Data (no align specified) Execute Read Write SECTION HEADER #3 FA name 1000 virtual size 1A000 virtual address 1F0 size of raw data 10 file pointer to raw data 417274 file pointer to relocation table 417277 file pointer to line numbers 7286 number of relocations 41 number of line numbers E0000060 flags Code Initialized Data (no align specified) Execute Read Write // //секция где находится таблица импорта // kd> db 41a000 L250 0041a000 4d 5a 4b 45 52 4e 45 4c-33 32 2e 44 4c 4c 00 00 MZKERNEL32.DLL.. 0041a010 50 45 00 00 4c 01 03 00-be b0 11 40 00 ad 50 ff PE..L......@..P. 0041a020 76 34 eb 7c 48 01 0f 01-0b 01 4c 6f 61 64 4c 69 v4.|H.....LoadLi 0041a030 62 72 61 72 79 41 00 00-18 10 00 00 10 00 00 00 braryA.......... 0041a040 00 50 00 00 00 00 40 00-00 10 00 00 00 02 00 00 .P....@......... 0041a050 04 00 00 00 00 00 39 00-04 00 00 00 00 00 00 00 ......9......... 0041a060 00 b0 01 00 00 02 00 00-00 00 00 00 02 00 00 00 ................ 0041a070 00 00 10 00 00 10 00 00-00 00 10 00 00 10 00 00 ................ 0041a080 00 00 00 00 0a 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a090 ee a1 01 00 14 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a0a0 ff 76 38 ad 50 8b 3e be-f0 a0 41 00 6a 27 59 f3 .v8.P.>...A.j'Y. 0041a0b0 a5 ff 76 04 83 c8 ff 8b-df ab eb 1c 00 00 00 00 ..v............. 0041a0c0 47 65 74 50 72 6f 63 41-64 64 72 65 73 73 00 00 GetProcAddress.. 0041a0d0 00 00 00 00 00 00 00 00-40 ab 40 b1 04 f3 ab c1 ........@.@..... 0041a0e0 e0 0a b5 07 f3 ab 8b 7e-0c 57 51 e9 ff 61 01 00 .......~.WQ..a.. 0041a0f0 56 10 e2 e3 b1 04 d3 e0-03 e8 8d 53 18 33 c0 55 V..........S.3.U 0041a100 40 51 d3 e0 8b ea 91 ff-56 4c 99 59 d1 e8 13 d2 @Q......VL.Y.... 0041a110 e2 fa 5d 03 ea 45 59 89-6b 08 56 8b f7 2b f5 f3 ..]..EY.k.V..+.. 0041a120 a4 ac 5e b1 80 aa 3b 7e-34 0f 82 ac fe ff ff 58 ..^...;~4......X 0041a130 5f 59 e3 1b 8a 07 47 04-18 3c 02 73 f7 8b 07 3c _Y....G..<.s...< 0041a140 00 75 f3 b0 00 0f c8 03-46 38 2b c7 ab e2 e5 5e .u......F8+....^ 0041a150 5d 59 46 ad 85 c0 74 1f-51 56 97 ff d1 93 ac 84 ]YF...t.QV...... 0041a160 c0 75 fb 38 06 74 ea 8b-c6 79 05 46 33 c0 66 ad .u.8.t...y.F3.f. 0041a170 50 53 ff d5 ab eb e7 c3-00 10 01 00 00 10 00 00 PS.............. 0041a180 f0 01 00 00 10 00 00 00-00 20 41 00 a7 72 41 00 ......... A..rA. 0041a190 03 00 00 00 60 00 00 e0-00 10 40 00 dc 72 41 00 ....`.....@..rA. 0041a1a0 00 80 00 00 00 20 01 00-04 54 00 00 00 02 00 00 ..... ...T...... 0041a1b0 00 10 40 00 ff 0f 41 00-04 74 41 00 60 00 00 e0 ..@...A..tA.`... 0041a1c0 46 12 41 00 fc 0f 40 00-00 10 00 00 00 a0 01 00 F.A...@......... 0041a1d0 f0 01 00 00 10 00 00 00-74 72 41 00 77 72 41 00 ........trA.wrA. 0041a1e0 86 72 41 00 60 00 00 e0-28 00 00 00 be 00 00 00 .rA.`...(....... 0041a1f0 00 00 00 00 00 00 00 00-00 00 02 00 00 00 e8 11 ................ 0041a200 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a210 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a220 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a230 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 0041a240 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ // //IMAGE_IMPORT_DESCRIPTOR // kd> dd 400000+1a1ee 0041a1ee 00000000 00000000 00000000 00000002 0041a1fe 000011e8 00000000 00000000 00000000 0041a20e 00000000 00000000 00000000 00000000 0041a21e 00000000 00000000 00000000 00000000 0041a22e 00000000 00000000 00000000 00000000 0041a23e 00000000 00000000 00000000 00000000 0041a24e 00000000 00000000 00000000 00000000 0041a25e 00000000 00000000 00000000 00000000 // //Import address table // kd> dps 400000+11e8 004011e8 6591de40 kernel32!LoadLibraryA 004011ec 6591e8c0 kernel32!GetProcAddress 004011f0 00000000 kd> da 400000 + 2 00400002 "KERNEL32.DLL"
Bazhan Вот спасибо, теперь все понятно... Код (Text): // //Import address table // kd> dps 400000+11e8 - адрес указывает в другую секцию (обычно все эти структуры лежат в одной секции) чего-то я сам не догадался загрузить и сдампить память - все в голове считал...