ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2

ANTICHAT — форум по информационной безопасности, OSINT и технологиям

ANTICHAT — русскоязычное сообщество по безопасности, OSINT и программированию. Форум ранее работал на доменах antichat.ru, antichat.com и antichat.club, сейчас доступен на antichat.xyz.
Вернуться   Форум АНТИЧАТ > ИНФО > Статьи > Авторские статьи
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

Оптимальный кодинг на Fasm
  #1  
Старый 14.08.2008, 12:26
slesh
Reservists Of Antichat - Level 6
Регистрация: 05.03.2007
Сообщений: 1,985
Провел на форуме:
3288241

Репутация: 3349


Отправить сообщение для slesh с помощью ICQ
По умолчанию Оптимальный кодинг на Fasm

СТАТЬЯ ПО ОПТИМАЛЬНОМУ КОДИНГУ В FASM
При использовании компилятора FASM можно достичь малых размеров программы, а при использовании дополнительных вещиц еще больше уменьшить размер программы.
1. Переменные в функциях:
При использовании переменных в функциях желательно не допускать следующих описаний:
Код:
proc myprog
 xor eax,eax
-.-.-.-.-.-.-.
 ret
 data_1 dw 0
 data_2 dd 0
 data_3 db 10 dup (0)
endp
Данный код будет не оптимален потому что переменные data_1,data_2,data_3 будут располагаться в коде программы в виде участка, что ведет к увеличению размера. Также данный код недопустим при вызове данных функций из потоков, т.к. может быть неконтрольное изменение данных переменных. Для решения данной проблемы необходимо чтобы вcе переменные располагались в стеке.

Код:
proc myprog
locals
 data_1 dw 0
 data_2 dd 0
 data_3 db 10 dup (0)
endl
 xor eax,eax
-.-.-.-.-.-.-.
 ret
endp
Небольшая поправка: описывать переменные необходимо следующим образом:
data_1 dw ?
data_2 dd ?
data_3 db 10 dup (?)
т.е. мы даем знать компилятору, что нас не волнует первоначальное значение данных переменных и => он не будет задавать им значение.

2. Структура секций:
Чаще всего структура программы выглядит так:
Код:
format PE GUI
entry _start

section '.code' code readable executable
_start:

section '.data' data readable writeable
data_1 dd 0
.-.-.-.-.

section '.idata' import data readable writeable
library ***
include ***
Данная структура наглядно показывает где и что находится в программе, но потребляет дополнительное место в exe файле. А именно:
Каждая секция физически имеет выравнение на 512 байт. => потери будут возникать как раз при выравнении, а т.к. у нас 3 секции, то это будет наблюдаться в трех местах программы. Исправляется это следующим образом:
Код:
format PE GUI
entry _start

section '.code' code import writeable readable executable
 library *** ;импорт
 include ***;импорт

_start: ; начало исполняемого кода
 xor eax,eax
.-.-.-.-.-.-
 ret / invoke ExitProcesse,0
; начало данных
 data_1 dd 0
 .-.-.-.-.
Как видно все 3 секции были помещены в одну. Сначала идут данные таблицы импорта, потом код программы а потом данные и переменные.
Недостатки данного способа в том, что секция кода имеет права на запись + нельзя совместить вместе секцию импорта, экспорта, ресурсов. Если присутствует хотя бы 2 из них, то необходимо так склеивать наименую из них.

3. Глобальные переменные:
Как было видно из вышеописанной структуры, секция данных находится в конце файла.
Допустим у нас есть множество переменных. Если эти переменные были описаны в самом конце программы как
data_1 dw ?
data_2 dd ?
data_3 db 10 dup (?)
то физически они не попадают в исполняемый файл и располагаются только в оперативной памяти.
по этому структура данных должна быть следующей
; инициализированные данные
xxx1 db 0
xxx2 dd 2
; неинициализированные данные
xxx3 db ?
xxx4 dd ?
4. Текст используемый в коде:
Допустим у нас есть строка:
invoke MessageBox,0,"Error","TITLE",0
После компиляции данный код будет выглядеть примерно так(но не всегда!)
Код:
 jmp _next
 data_1 db "Error",0
 data_2 db "TITLE",0
_next:
 push 0
 push data_2
 push data_1
 push 0
 call MessageBox
Как мы видим появилась лишняя инструкция jmp _next для обхода блока данных.
На реале получается еще более страшный код. Именно по этой причине лучше всего такие текстовые строки описывать
как переменные.
т.е.
Код:
invoke MessageBox,0,data_1,data_2,0
.-.-.-.-.-
;data
 data_1 db "Error",0
 data_2 db "TITLE",0
Теперь не будет в коде таких левых команд
5. Оптимизация команд:
Не секрет, что существуют команды которые при определенных условиях выполняют одну и туже операцию, но занимают меньше места
Среди таких команд можно перечислить часто используемые:
1. add/sub reg,1 лучше заменить на inc/dec reg
2. умножение/деление на степень двойки. - shl/shr reg,1 ; где 1,2,3 - степень
3. При записи в регистр какого либо числа командой mov, лучше всего использовать по возможности регистр eax т.к.
mov eax,xxxxxxxxh имеет более короткий опкод, нежили mov ecx,xxxxxxxxh
4. mov reg,0 заменяется на xor reg,reg
5. cmp reg,0 заменяться на test reg,reg
6. Для циклов использовать лучше использовать регистр ecx с командами loop и jzecx
7. Адресация данных. Допустим у нас есть массив и значения некоторых элементов нам нужно поместить в стек
для этого используем адресацию через регистр.
lea eax,[mas]
push [eax+4]
push [eax+8]
push [eax+12]
дело втом что адресация через регистры имеет более короткий опкод.
8. При использовании процедур желательно в начале и в конце использовать pushad/popad а не по отдельности сохранять значения изменяемых регистров.
6. Таблица импорта:
При написании программ с большим числом API функций лучше всего использовать динамический импорт.
Это связанно с тем, что можно более оптимально распределить данные.
В итоге таблица импорта должна будет состоять только из 2-х функций - GetProcAddress и LoadLibraryA
и тогда код будет строиться следующим образом:
Код:
invoke LoadLibraryA,lib_1
mov ebx,eax
invoke GetProcAddress,ebx,proc_1
mov [proc_1_adr],eax
invoke GetProcAddress,ebx,proc_2
mov [proc_2_adr],eax
.-.-.-.-
invoke proc_1_adr,param1,param2
.-.-.-.-.
lib_1 db 'LIBNAME.DLL',0
proc_1 db 'PROC_NAME_1',0
proc_2 db 'PROC_NAME_2',0
.-.-.-.-.-.-.
proc_1_adr dd ?
proc_2_adr dd ?
Данные изменения будут заметны только при большом числе функций.
Плюсы данного метода - при первичном осмотре файла средствами типа PE_edit будут незаметны используемые функции.
Также для большей скрытности имена функций и библиотек могут быть зашифрованные.
Дополнение: Также можно использовать вместо GetProccAddress другие алгоритмы нахождения адресов функций через таблицу экспорта библиотеки.
А при использовании метода импорта по хешам, это значительно сократит размер программы в ненадобности хранения полного имени API функции

Также удобно использовать собственное построение таблицы импорта т.к. оно занимает чуть меньше места
а именно:
Код:
dd 0,0,0,IT_lib-IMAGE_BASE,IT-IMAGE_BASE
dd 0,0,0,0,0
IT:
 mLoadLibrary	  dd  _mLoadLibrary-IMAGE_BASE
  dd 0
IT_lib db 'KERNEL32.DLL',0
_mLoadLibrary dw 0 ; HINT
 db 'LoadLibraryA',0,0 ; NAME
7. Аналоги стандартный API:
Некоторые Api функции созданы только для удобства получения некоторых данных. При этом на код самой функции может быть потрачено меньше байт, чем на поиск её адреса и вызова.
Примером таких функции являются:
1. invoke GetModuleHandleA,0
Её можно заменить на:
mov eax,[fs:18h]
mov eax,[eax+30h]
mov eax,[eax+3h]
2. invoke GetProcessHeap
Можно заменить на:
mov eax,[fs:18h]
mov eax,[eax+30h]
mov eax,[eax+18h]
3. invoke GetLastError
заменяем на
mov eax,[fs:18h]
mov eax,[eax+34h]
4. invoke lstrlen,my_str
в данном случаи код на ручной подсчет длинны строки будет меньше чем затраты на импорт функции

Таким образом можно оптимизировать использование и некоторых других функций.
8. Разбиение кода на функции и "кеширование":
Порой часто встречается, что необходимо выполнять похожие операции, для этого лучше разбивать код на функции которые будут делать данную операцию
Примером таких функций может быть функция отсыла запроса на http сервер.
В случаях, когда код чуть отличается, то можно делать специальные флаги которые будут указывать какой вариант использовать.
т.е. логика будет такая:
Код:
cmp param_2,0
je _next
.-.-.predoperation.-.-.-
_next:
.-.-.operation.-.-.-
 ret
Также не малую важную роль играет кеширование некоторых значений.
К примеру для сетевых приложений желательно кешировать IP адрес сервера, а не резолвить его каждый раз при подключении.
Тоже самое можно делать и с некоторыми API функциями. Которые используются в разное время, но возвращают одно и тоже.
Примером может быть:
Код:
invoke GetModuleHandleA,0
mov [mhandle],eax
.-.-.-.-
mov eax,[mhandle] ; вместо вызова функции GetModuleHandleA
.-.-.-.-.
mov eax,[mhandle] ; вместо вызова функции GetModuleHandleA
.-.-.-.-.
mhandle dd ?
В это таким образом можно не только у уменьшить размер программы, но и увеличить быстродействие.
9. Использование альтернативного MZ заголовка:
Стандартный MZ заголовок у FASM имеет размер 128 байт. ПРи использовании альтернативного заголовка этот размер может быть уменьшен до 64 байт, без прибегания к разным хитростям.
делается это с помощью команды: format PE GUI on 'stub.inc'
примером такого стаба может быть следующий код (переведен в HEX)
Код:
 4D 5A 00 00 01 00 00 00 
 02 00 00 00 FF FF 00 00
 40 00 00 00 00 00 00 00
 40 00 00 00 00 00 00 00 
 B4 4C CD 21 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
 00 00 00 00 00 00 00 00
Перед его использование его нужно перевести из HEX в BIN

ЗАКЛЮЧЕНИЕ
Конечно это не все способы которые помогут уменьшить размер программы, но всё же в совокупности они могут дать уменьшение размера программы на несколько килобайт, что немало важно бывает в некоторые специфических областях программирования.
(С) SLESH 2008

Последний раз редактировалось slesh; 14.08.2008 в 13:02..
 
Ответить с цитированием

  #2  
Старый 14.08.2008, 16:46
dmnt
Познающий
Регистрация: 06.06.2007
Сообщений: 99
Провел на форуме:
559723

Репутация: 94
Отправить сообщение для dmnt с помощью ICQ
По умолчанию

Цитата:
ПРи использовании альтернативного заголовка этот размер может быть уменьшен до 64 байт, без прибегания к разным хитростям.
делается это с помощью команды: format PE GUI on 'stub.inc'
примером такого стаба может быть следующий код (переведен в HEX)
что-то у меня фасм от такого стаба начал делать тестовый файл в 1,5Кб весом.
а со стандартным стабом делает его в 1024байта.
 
Ответить с цитированием

  #3  
Старый 14.08.2008, 22:24
slesh
Reservists Of Antichat - Level 6
Регистрация: 05.03.2007
Сообщений: 1,985
Провел на форуме:
3288241

Репутация: 3349


Отправить сообщение для slesh с помощью ICQ
По умолчанию

Это глюки новой версии компилятора. сам заметил её. Под Win при версии 1.65.14 всё нормально работает.

P.S. фактически привинчивание нового стаба не приведет к уменьшению программы, это просто уберет левые данные, такие как This program cannot be run in DOS mode.

Последний раз редактировалось slesh; 15.08.2008 в 09:19..
 
Ответить с цитированием

  #4  
Старый 30.04.2009, 19:51
eLWAux
Members of Antichat - Level 5
Регистрация: 15.06.2008
Сообщений: 941
Провел на форуме:
5111568

Репутация: 2399


Отправить сообщение для eLWAux с помощью ICQ
По умолчанию

стаб, который занимает на 4байта меньше стаба слеша

Код:
4D 5A 3C 00 01 00 00 00  02 00 00 01 FF FF 02 00   MZ<.........яя..
00 10 00 00 00 00 00 00  1C 00 00 00 00 00 00 00   ................
0E 1F BA 0E 00 B4 09 CD  21 B8 01 4C CD 21 44 4F   ..є..ґ.Н!ё.LН!DO
53 20 69 73 20 64 65 61  64 0D 0A 24               S is dead..$
в fasm'е:
format PE GUI on 'stub'
 
Ответить с цитированием

  #5  
Старый 30.04.2009, 21:07
gibson
Moderator - Level 7
Регистрация: 24.02.2006
Сообщений: 447
Провел на форуме:
2872049

Репутация: 705
Отправить сообщение для gibson с помощью ICQ
По умолчанию

Stub 0Ch (c) wasm.ru
Код:
00000000 4D 5A 00 00 01 00 00 00 01 00 00 00 50 45 00 00 MZ..........PE..
00000010 4C 01 07 00 08 00 00 00 B0 21 CD 29 B4 4C CD 21 L........!.).L.!
00000020 E0 00 0E 01 0B 01 04 14 20 00 00 00 00 2E 00 00 ........ .......
00000030 18 00 00 00 00 E0 00 00 00 10 00 00 0C 00 00 00 ................
вообще лучше всего использовать пакеры, которые умеют уменьшать FileAlign или хотябы использовать его более мение рационально.
 
Ответить с цитированием

  #6  
Старый 30.04.2009, 22:23
FrMn
Познающий
Регистрация: 08.08.2008
Сообщений: 55
Провел на форуме:
369629

Репутация: 54
По умолчанию

slesh, вы б хоть указале, где по размеру, а где по скорости оптимизация. насчет апи - так можно вобще хеши зашить, исчо меньше места занимать будет.
вобщем как вам, так и остальным рекомендую почитать маны от Intel'a касающиеся оптимизации, а также посмотреть формат РЕ более детально и провереть все, что вы здесь напесале, а то только сбиваете с толку юнных одептов.
 
Ответить с цитированием

  #7  
Старый 04.05.2009, 23:21
slesh
Reservists Of Antichat - Level 6
Регистрация: 05.03.2007
Сообщений: 1,985
Провел на форуме:
3288241

Репутация: 3349


Отправить сообщение для slesh с помощью ICQ
По умолчанию

Там где оптимизация идет по размеру, зачастую она идет и по скорости.
Цитата:
вобщем как вам, так и остальным рекомендую почитать маны от Intel'a касающиеся оптимизации, а также посмотреть формат РЕ более детально и провереть все, что вы здесь напесале, а то только сбиваете с толку юнных одептов.
Оптимизация по опкодам - это я только привел примеры таких вариантов, а не все их. Хотябы для того, чтобы люди хоть знали что есть разница.
А на счет PE - тут уже вы переборщили. Как ни крути его, но PE он и остается PE.
В статье я только раскрыл те моменты, которые люди могут сами(без особых проблем) реализовать. Если дело на то пошло, то можно и самому собираться MZ и PE заголовок. И оставить только одну таблицу для единственной секции, сразу за которой пойдет код этой секции. Но собирать всё вручную - это геморно, и уже выходит за рамки данной статьи.
 
Ответить с цитированием
Ответ



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Fasm Pr0t0tipeR С/С++, C#, Delphi, .NET, Asm 4 09.03.2008 23:42
Кодинг на php. Настройка локалхоста. procedure *nix 6 08.01.2008 15:35



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ