Есть задача пропатчить существующую dll. Для этого добавил в неё дополнительную секцию и теперь хочу дополнить её своим кодом а также врезками в код, существующий в других секциях. Для этого пытаюсь сделать следующее. Пишу свою библиотеку и пытаюсь имитировать структуру существующей, т.е. создать тот-же набор секций, находящихся по тем-же виртуальным смещениям, что и секции в исходной библиотеке. По идее это должно упростить последующий перенос фрагментов кода из моей либы в целевую. Использую masm32. Это выглядит примерно так (в сокращённом виде): Код (Text): .xmm .686p .model flat OPTION LANGUAGE:STDCALL _text segment public 'CODE' use32 assume cs:_text assume ds:_data org 1000h LibMain proc instance:DWORD,reason:DWORD,unused:DWORD ;... ret LibMain endp _text ends _data segment public 'DATA' use32 assume cs:_data org 600000h db 0FFh _data ends _MyCode segment public 'CODE' use32 assume cs:_text assume ds:_data org 60D000h nop _MyCode ends end LibMain Т.е. видно, что в секциях я расставил директивы org, и между двумя секциями кода поместил секцию данных. Однако после ассемблирования и линковки получаю совсем не то, что нужно. Видно, что обе секции кода находятся вместе в начале, и после них размещена секция данных, а виртуальные смещения не имеют ничего общего с адресами из директив org. Непонятно, как заставить ассемблер/линковшик создать секции именно там, где мне надо.
malandrinus Директива org, это вроде как рудимент оставшийся от 16-битной версии. На самом деле masm не может знать виртуальных аддресов, потому-что ассемблирует объектные модули. Линкеру можно явно указать базу, а также выравнивание виртуальных и файловых секций, чего как правило достаточно для эмулирования обычных т.е. не хакерских .dll`eй.
Шоу только начинается. Полагаю, следующим номером будет вопрос, как впилить в старую dll новые релоки.
A что такой не вежливый. Надо-ли вспоминать кто обычно определяет содержимое, а за одно и разме р секции.
GoldFinch, Это рабочая метода. Так уже делают (автор не я), и она работает, хотя есть некоторые неудобства. Почему именно fasm? Где можно почитать о методе, который вы упоминаете? CyberManiac, Это ко мне? Нет, не спрошу. С такой рутиной как-нибудь сам справлюсь. Меня интересовал конкретный вопрос. Кто-нибудь помнит, о чём он был?
учите формат PE, асм, инструменты и не топчите моск честным людям %) метода кнешно рабочая, но она гуан*.
Идея, в общем-то, простая. В fasm есть директива file, ей можно нарезать исходный файл хоть вдоль, хоть поперёк. Вот пример, иллюстрирующий это. Код (Text): format binary as "dll" file "%WinDir%\system32\user32.dll": 0, 0x61B14; смещение VS_FIXEDFILEINFO.dwFileVersionLS dw 3100; апгрейдим с 3099 до 3100 file "%WinDir%\system32\user32.dll": $, 0x61C5E-$; смещение билда в строковой FileVersion du "3100" file "%WinDir%\system32\user32.dll": $; остаток файла Смещения забиты жёстко, но интерпретатор fasm позволяет разобрать структуру PE и собрать то, что нужно. Ключевые директивы: virtual, file, load и store, причём последняя может упростить патч до Код (Text): file "%WinDir%\system32\user32.dll"; оптом дешевше store word 3100 at 0x61B14 store qword "3 1 0 0" and 0xFF00FF00FF00FF at 0x61C5E; маска прибъёт пробелы Неленивый и IMAGE_OPTIONAL_HEADER.CheckSum может поправить, но это уж кружавчики.
baldr Ленивый тоже может. Код (Text): include 'general.inc' format binary as "dll" IDH.e_lfanew equ 3Ch sizeof.Signature equ 4h sizeof.FileHeader equ 14h IOH.CheckSum equ 40h PEFile: file "%WinDir%\system32\user32.dll" PESize = $-PEFile load PEHeader dword from PEFile + IDH.e_lfanew PEHeader = PEFile + PEHeader PEOptionalHeader = PEHeader + sizeof.Signature + sizeof.FileHeader ;set the checksum field to zero store dword 0 at PEOptionalHeader + IOH.CheckSum newCheckSum = 0 repeat PESize/4 load buf dword from (%-1)*4 newCheckSum = (newCheckSum + buf) and 0FFFFFFFFh + (newCheckSum + buf) shr 32 end repeat newCheckSum = (newCheckSum shr 16) + (newCheckSum and 0FFFFh) newCheckSum = newCheckSum and 0FFFFh + newCheckSum shr 16 + PESize store dword newCheckSum at PEOptionalHeader + IOH.CheckSum display 'New checksum is: ',h=newCheckSum,10,13 Модификация display отсюда.
Тебе лиж-бы по холиварить. Свет клином на этом fasm`e сошелся. Первая метода, очень хорошая. Хотя конечно те кто не в состоянии сообразить как правильно нарезать файлы без директивы file, могут думать иначе. При умелой доработке метода, код для дополнительной секции можно и на C++ писать.
Это делается например так: Код (Text): """ EXE file path example, using pefile library by Ero Carrera (http://code.google.com/p/pefile/) and FASM compiler (http://flatassembler.net/) """ import os import tempfile import pefile # file to patch FILE_NAME = 'some.exe' # we can't know patch size before we compile it, so we should define it before MAX_PATCH_SIZE = 0x100 # path to FASM without backslash, like 'c:\fasm' FASM = '%FASM%' TEMP_FILENAME = tempfile.gettempdir() + '\\patch' pe = pefile.PE(FILE_NAME) text = pe.sections[0] textFileSize = text.SizeOfRawData + (0x1FF - (text.SizeOfRawData + 0x1FF) & 0x1FF) # aligned by 0x200 patchRVA = text.VirtualAddress + textFileSize - MAX_PATCH_SIZE k32imp = (entry.imports for entry in pe.DIRECTORY_ENTRY_IMPORT if entry.dll.lower().startswith('kernel32')).next() def get_imp_va(funcName): return hex((imp.address for imp in k32imp if imp.name == funcName).next()) open(TEMP_FILENAME + '.asm ', 'w').write('''; THIS CODE WAS GENERATED BY SCRIPT use32 label _LoadLibrary dword at ''' + get_imp_va('LoadLibraryA') + ''' label _FindFirstFile dword at ''' + get_imp_va('FindFirstFileA') + ''' label _FindNextFile dword at ''' + get_imp_va('FindNextFileA') + ''' label _OriginalEntryPoint at ''' + hex(pe.OPTIONAL_HEADER.AddressOfEntryPoint + pe.OPTIONAL_HEADER.ImageBase) + ''' include "''' + FASM + '''\include\win32a.inc" org ''' + hex(pe.OPTIONAL_HEADER.ImageBase + patchRVA) + ''' align 16 ; NewEntryPoint call LoadPlugins call _OriginalEntryPoint ret mask db "*.plgn", 0 align 16 proc LoadPlugins uses edi local findData:WIN32_FIND_DATA lea eax, [findData] invoke _FindFirstFile, mask, eax mov edi, eax test eax, eax jnz load_loo ret load_loo: lea eax, [findData.cFileName] invoke _LoadLibrary, eax lea eax, [findData] invoke _FindNextFile, edi, eax test eax, eax jnz load_loo ret endp ''') err = os.system(FASM + '\\fasm.exe ' + TEMP_FILENAME + '.asm ' + TEMP_FILENAME + '.bin') os.remove(TEMP_FILENAME + '.asm') if err == 0: print "\nCompiled OK. Patching." pe.set_bytes_at_rva(patchRVA, open(TEMP_FILENAME + '.bin', 'rb').read()) pe.OPTIONAL_HEADER.AddressOfEntryPoint = patchRVA pe.write(FILE_NAME) os.remove(TEMP_FILENAME + '.bin') print "All done." os.system("pause") Сделать так же для длл с релоками не проблема.
l_inc, Ленивый может и editbin /release сделать. Я всё не доведу до ума обёртку для display, понимающую синтаксис «display "$=", %#010x:$, 13, 10». Всё-таки привычку думать форматами printf() трудно изжить.
baldr На измену, значит, подбиваете? То ли я далёк от printf, то ли не очень оно похоже на его формат. В любом случае никогда не сталкивался с необходимостью расширять синтаксис вывода аж настолько. Это ведь не пользовательский вывод, а для себя только.
l_inc, То, что до двоеточия — легальная спецификация формата printf, означает вывести шестнаццатирично мелкими буквами с соответствующим префиксом ("0x") в поле шириной 2+8=10 с лидирующими нулями. Ну а $ — известно что. Привычка, я ж говорю. Лет двадцать назад я Фортрановскими форматами думал. Ох, чую, дадут пинка за оффтоп.
baldr Вообще, я почему-то решил, что в имитацию поведения printf входит также и обработка того, что в кавычках (ну там сохранили переданную строку "$=" директивой db в секции virtual, потом через load распарсили посимвольно и т.д.). Т.е. собственно спецификаторы должны были бы быть в кавычках. В общем, извиняюсь, неправильно понял. А спецификатор %#010x знамо, что делает, разумеется. Сим предлагаю оффтоп и завершить.