fasm как генератор патчей

Тема в разделе "WASM.ASSEMBLER", создана пользователем GoldFinch, 10 сен 2008.

  1. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Известно, что фасм по умолчанию генерит .bin файл, содержащий код без каких-либо заголовков, т.е. если написать в фасме "use32/pushd 0x12345678", после нажатия shift-f9 он сгенерит файл из 5 байт: "68 78 56 34 12". Но каждый раз, переключаясь на коммандер, и нажимая просмотр (F3-3) мне вспоминалось что в фасме есть замечательные директивы "load ..." и "display". В результате были написаны макросы:
    Код (Text):
    1. macro _display_byte_hex_ prefix,B,postfix {
    2.       local C
    3.       C="0"+(B) shr 4
    4.       if C>"9"
    5.          C=C+"A"-"9"-1
    6.       end if
    7.       display prefix,C
    8.       C="0"+(B) and 0x0F
    9.       if C>"9"
    10.          C=C+"A"-"9"-1
    11.       end if
    12.       display C,postfix }
    13. macro _dump_ A,N,delim {
    14.       local B
    15.       repeat N
    16.              load B byte from A+%-1
    17.              _display_byte_hex_ "",B,delim
    18.       end repeat
    19.       display 13,10 }
    20. macro PATCH va* {
    21.       use32
    22.       virtual at va
    23.       display "-----[ Begin of Patch at ",`va," ]-----",13,10
    24.       define _need_line_ 0}
    25. macro DUMP_PATCH delim* {
    26.       match =1,_need_line_ \{display "-----------------------",13,10\}
    27.       _dump_ $$, $-$$, delim
    28.       _display_byte_hex_ "-----[ End of Patch, size = 0x", $-$$, <" ]-----",13,10>
    29.       end virtual }
    30. macro P [line]{
    31. common local A
    32.        A=$
    33.        line
    34.        _display_byte_hex_ "+",A-$$,":"
    35.        irps e,line \{ display " ",\`e \}
    36.        display "; "
    37.        _dump_ A, $-A, " "
    38.        define _need_line_ 1 }
    которые для файла
    Код (Text):
    1. include "patch.inc"
    2.  
    3. PATCH 0x12345678
    4. pushd 0x12345678
    5. popd [0xABCDEF]
    6. DUMP_PATCH ","
    7.  
    8. PATCH 0x30001000
    9. P pushd 0x12345678
    10. P popd [0xABCDEF]
    11. DUMP_PATCH ""
    выдают
    Код (Text):
    1. -----[ Begin of Patch at 0x12345678 ]-----
    2. 68,78,56,34,12,8F,05,EF,CD,AB,00,
    3. -----[ End of Patch, size = 0x0B ]-----
    4.  
    5.  
    6. -----[ Begin of Patch at 0x30001000 ]-----
    7. +00: pushd 0x12345678; 68 78 56 34 12
    8. +05: popd [ 0xABCDEF ]; 8F 05 EF CD AB 00
    9. -----------------------
    10. 68785634128F05EFCDAB00
    11. -----[ End of Patch, size = 0x0B ]-----
    Может у кого есть идеи по улучшению данного метода создания патчей?
     
  2. nobodyzzz

    nobodyzzz New Member

    Публикаций:
    0
    Регистрация:
    13 июл 2005
    Сообщения:
    475
    похожая идея http://wasm.ru/article.php?article=patchbyfasm
     
  3. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    все же это совершенно разные вещи.
     
  4. Freeman

    Freeman New Member

    Публикаций:
    0
    Регистрация:
    10 фев 2005
    Сообщения:
    1.385
    Адрес:
    Ukraine
    действительно полезная штука, надо будет какнибуть обкатать, GoldFinch, респект
     
  5. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Изначально идея была такой: получить в фасме байты патча, скопипастить их например в винхекс, и уже им патчить. Но потом меня осенило, что фасм все же предназначен для написания прог, и я подумал, что бы фасму не генерить прогу которая сама пропатчит файл. После нескольких дней копания в хелпе фасма был сделан инклуд patch.inc (см. аттач). В нем содержится набор макросов позволяющих выполнять следующие задачи:
    - отображение байт откомпилированного кода в окне вывода компилятора,
    - запись байт откомпилированного кода в целевой PE файл.

    Для этого используются две конструкции, первая:
    DUMP address
    [_D] codeline1
    ...
    DUMP_END
    Компилирует строки между DUMP и DUMP_END, считая что они начинаются с адреса address. Выводит размер блока в байтах, группирует байты в двойные слова и выводит примерный код патча:
    mov dword[address], dw1 / mov dword[address+4], dw2 / ...
    Если строка начинается с _D, в окне компилятора выводятся байты этой строки.

    Вторая конструкция
    FILE "file.name"
    PATCH address1
    codeline1 ;фрагмент 1
    ...
    PATCH address2
    codeline2 ;фрагмент 2
    ...
    PATCH_END
    Эта конструкция создает программу, которая записывает в файл "file.name" байты кода фрагментов, пересчитывая виртуальные адреса address в файловые смещения.
    Если address меньше rva 1й секции, он рассматривается как файловое смещение.
    При запуске программа выводит список патчей (адрес:размер) и спрашивает подтверждение патча файла.
    Также, при разборе заголовка целевого PE файла создаются константы
    __base - база файла
    __entrypoint - виртуальный адрес точки входа
    __import - виртуальный адрес таблиц импорта
    __PE - смещение PE заголовка в файле
    __section_table - смещение таблицы секций в файле
     
  6. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Пример использования.
    Есть запакованная длл, в ее запакованной области по VA 0x30ABCDE6 расположен вызов функции, которая почемуто возвращает не 1 а 0.
    Создаем "скрипт"
    Код (Text):
    1. include "patch.inc"
    2. ;0) mem patch, replaces
    3. ;___:30ABCDE6 00C                 call    Get_@eax_AL
    4. ;___:30ABCDEB 00C                 mov     _xxx_, al
    5. DUMP 0x30ABCDE6
    6. _D   mov al,1
    7. _D   jmp short 0x30ABCDEB
    8. DUMP_END
    компилим, в окне компилятора видим
    Ищем в секции ресурсов место с адресом покрасивее, и дописываем в наш "скрипт":
    1) разрешение выполнения кода в секции ресурсов
    2) замена адрес перехода на точке входа
    3) код подмены адреса возврата из DllEntry() и патча нужных байт
    Код (Text):
    1. FILE "target.dll"
    2.  
    3. ;1) fix .rsrc (#3) section characteristics
    4. PATCH __section_table+0x24+(3-1)*0x28
    5. dd 0xE0000040
    6.  
    7. ;2) DllEntryPoint, replaces
    8. ;jmp 0x3006759D ;EP_0
    9. PATCH __entrypoint
    10. jmp _new_EP
    11.  
    12. ;3) Main code
    13. PATCH 0x30062700
    14. _new_EP:
    15. popd [_saved_ret_addr]
    16. pushd _make_patch
    17. jmp 0x3006759D ;EP_0
    18. _make_patch:
    19. mov dword[0x30ABCDE6], 0x01EB01B0 ;<<<< mem patch
    20. jmp dword[_saved_ret_addr]
    21. align 4
    22. _saved_ret_addr dd ?
    23.  
    24. PATCH_END
    Компилим, запускаем, видим окошко "...нажмите ОК чтобы патчить",
    и получаем пропатченый файл.
     
  7. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    После долгих медитаций мне открылось, что все вышенаписанное есть муть, т.к. неоптимально и избыточно.

    Во-первых о патчах в памяти. Конструкция DUMP va / <код> /END_DUMP, выводящая в окне компилятора число байт откомпилированного кода, с кодом самого патча (mov [va],xxx), интересна, но:
    1) mov [va],xxx надо вручную кобпировать в нужное место
    2) если какой-то патч в памяти занимает больше 4х байт, то это должен быть jmp или call на код перехвата (5 байт). В крайнем случае это должен быть mov edi,va/../rep movs.
    Тем не менее, задача патча в памяти остается, поэтому есть смысл встраивать код патча в памяти в основной код патча: MEM va/dd 1,2/END_MEM. Результатом работы такой конструкции будет код mov dword[va],1/mov dword[va+4],2.
    Также есть смысл в наборе отдельных макросов под частные задачи, например подмена относительных адресов перехода в памяти:
    macro FIX_REL va,hook { mov dword[va+1],hook-(va+5) }

    Во-вторых, макрос "_D xxx" компилящий строку xxx и выводящий ее байты, хорош сам по себе, и может использоваться в любом месте кода. Кроме того, для него нужна куча других макросов, поэтому его можно засунуть в отдельный инклуд "%fasminc%\tool.inc".

    Во-третьих, необходимо большое количество мелких изменений, улучшающих синтаксис скрипта патча, например макрос
    macro DWORD raw,value{ PATCH raw / dd value }
    Возможно следует устанавливать базу программы равной базе целевого файла, чтобы в коде патча работала директива rva. Также, следует пересмотреть имена макросов.

    Пока что, пример скрипта выглядит так:
    Код (Text):
    1. include "%fasminc%\patch.inc"
    2. FILE "xxx.dll"
    3. DWORD __section_table+0x24+(3-1)*0x28, 0xE0000040 ;1) fix .rsrc (#3) section characteristics
    4. DWORD __entrypoint_ofs,rva _new_EP ;2) Fix DllEntryPoint
    5. PATCH 0x30062700 ;3) Main code
    6. _new_EP:
    7. popd [_saved_ret_addr]
    8. pushd _make_patch
    9. jmp __entrypoint
    10. ;---------------------------
    11. _make_patch:
    12. ;m1) replace ___:30033B26  call Get_@eax_AL
    13. ;           ___:30033B2B  mov  _xxx_, al
    14. MEM 0x30033B26
    15.    mov al,1
    16.    jmp short 0x30033B2B
    17. MEM_END
    18. ;m2) set function hook, replace ___:3003ABCD call 3003DEF0
    19. FIX_REL 0x3003ABCD,_hook
    20. jmp dword[_saved_ret_addr]
    21. ;---------------------------
    22. _hook:
    23. ...
    24. jmp 0x3003DEF0 ;original func
    25. ;---------------------------
    26. align 4
    27. _saved_ret_addr dd ?
    28. PATCH_END
     
  8. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Чтоб зря не валялось... инклуд tool.inc
    В нем макрос "DUMP <строка>", который выводит текущее смещение ($), саму строку и откомпилированные байты строки в окно компилятора (+их количество). Можно использовать просто "DUMP" чтобы посмотреть $.
    Также в инклуде есть макрос INLINE - позволяет писать несколько инструкций в одной строчке, через "/". Макросы можно использовать совместно, т.е. DUMP INLINE xxx/yyy/zzz

    Пример использования:
    Код (Text):
    1. include "%fasminc%\tool.inc"
    2. use32
    3. db 0x23 dup (0)
    4. DUMP ;displays "00000023:"
    5. DUMP jmp $ ;displays "00000023: jmp $; EB FE (#02)"
    6.  
    7. ;displays "00000025: INLINE mov ecx , 5 / @@ : push ecx / loop @r; B9 05 00 00 00 51 E2 FD (#08)"
    8. DUMP INLINE mov ecx,5 / @@:push ecx /loop @r
     
  9. GoldFinch

    GoldFinch New Member

    Публикаций:
    0
    Регистрация:
    29 мар 2008
    Сообщения:
    1.775
    Не совсем по теме, но в продолжение идеи макроса выводящего откомпилированный код
    Код (Text):
    1. macro DUMP.BEGIN comment{DUMP.start=$}
    2. macro DUMP.END {
    3.     local ..B,..C,..N
    4.     ..N=$-DUMP.start
    5.     display ";========================",13,10
    6.     repeat ..N
    7.         if ((%-1) and 0x0F)=0
    8.             display "db "
    9.         end if
    10.         load ..B byte from DUMP.start+%-1
    11.         C="0"+(..B) shr 4
    12.         if C>"9"
    13.             C=C+"A"-"9"-1
    14.         end if
    15.         display "0x",C
    16.         C="0"+(..B) and 0x0F
    17.         if C>"9"
    18.             C=C+"A"-"9"-1
    19.         end if
    20.         display C
    21.         if ((%-1) and 0xF)=0xF
    22.             display 13,10
    23.         else if %=..N
    24.             display 13,10
    25.         else
    26.             display ","
    27.         end if
    28.     end repeat
    29.     display ";========================",13,10}
    - пара макросов выводящих код между собой в виде
    db 0x6A,0xF5,0xFF,0x15,0x3C,0x10,0x40,0x00,0xA3,0x60,0x1B,0x40,0x00,0x6A,0xF6,0xFF
    db 0x15,0x3C,0x10,0x40,0x00 ...

    Полезно для скрытия исходного кода в исходнике. Скомпилил, вставил вместо кода его байты и никто не узнает что там было :)
     
  10. Semiono

    Semiono Member

    Публикаций:
    0
    Регистрация:
    27 ноя 2005
    Сообщения:
    768
    +1
    а я это искал! а мне сказали - это не линукс вей :-]
    вот это я понимаю настоящий (hex) patch!