Инструменты:
- FASM (или любой ассемблер с возможностью создания .bin файлов - fasm, nasm, yasm, etc).
- Python, версии 2.5-2.7, в т.ч. IronPython.
- Библиотека pefile. (работает только с Python'ом 2.5-2.7).
- Любое средство позволяющее убедиться что в файле есть место под патч и нужные импорты.
""" 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 = 0x80
# 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())
asmFile = open(TEMP_FILENAME + '.asm ', 'w')
asmFile.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
''')
asmFile.close()
err = os.system(FASM + '\\fasm.exe ' +
TEMP_FILENAME + '.asm ' + TEMP_FILENAME + '.bin')
os.remove(TEMP_FILENAME + '.asm')
if err == 0:
print "\nCompiled OK. Patching."
binfile = open(TEMP_FILENAME + '.bin', 'rb')
pe.set_bytes_at_rva(patchRVA, binfile.read())
pe.OPTIONAL_HEADER.AddressOfEntryPoint = patchRVA
pe.write(FILE_NAME)
binfile.close()
os.remove(TEMP_FILENAME + '.bin')
print "All done."
os.system("pause")
Замечания по коду:
- Код совместим с Python 2.5 по этому там нет with.
- Код совместим с IronPython по этому там явно вызываются close() для файлов. В обычном пайтоне можно писать
pe.set_bytes_at_rva(patchRVA, open(TEMP_FILENAME + '.bin', 'rb').read())
хендлы закроются сами. - Разумеется это простейший пример, для .exe файла без релоков, у которого уже импортированы нужные функции, впрочем с помощью pefile можно решать и более сложные задачи.
- Запись в конец секции плохо выглядит с точки зрения антивирусов. По хорошему надо как минимум патчить сразу за концом оригинального кода, чтобы не было большого разрыва.
No comments:
Post a Comment