Оцените код

Тема в разделе "WASM.BEGINNERS", создана пользователем Phuntik, 26 дек 2009.

  1. Phuntik

    Phuntik New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2008
    Сообщения:
    318
    Собственно вот ЭТО:
    Код (Text):
    1. format PE console 4.0
    2.  
    3. include 'win32ax.inc'
    4.  
    5. struct COORD
    6.     x dw ?
    7.     y dw ?
    8. ends
    9.  
    10. struct SMALL_RECT
    11.     Left   dw ?
    12.     Top    dw ?
    13.     Right  dw ?
    14.     Bottom dw ?
    15. ends
    16.  
    17. struct CONSOLE_SCREEN_BUFFER_INFO
    18.     dwSize              COORD <>
    19.     dwCursorPosition    COORD <>
    20.     wAttributes         dw ?
    21.     srWindow            SMALL_RECT <>
    22.     dwMaximumWindowSize COORD <>
    23. ends
    24.  
    25. entry start
    26.  
    27. ;**********************************************************************************************************************
    28. section '.text' code readable executable
    29.  
    30.  start:
    31.     invoke GetStdHandle, STD_OUTPUT_HANDLE          ; Получаем хэндл окна вывода
    32.     mov [outputHandle], eax                 ; Сохраняем
    33.      
    34.     invoke GetStdHandle, STD_INPUT_HANDLE
    35.     mov [inputHandle], eax
    36.      
    37.     invoke VirtualAlloc, 0, 4096, MEM_COMMIT, PAGE_READWRITE    ; Резервируем память под вводимую строку
    38.     mov [buffer], eax
    39.     .if eax = 0
    40.         stdcall WriteLnStringToConsole, 'Error allocation memory'
    41.         jmp lExit
    42.     .endif
    43.      
    44.     stdcall WriteLnStringToConsole, 'Hello'
    45.      
    46.     .while TRUE
    47.         stdcall WriteStringToConsole, '-'
    48.         stdcall ReadStringFromConsole, [buffer], 512
    49.          
    50.         ; Выход
    51.         invoke lstrcmp, dword[buffer], comExit
    52.         .if eax = 0
    53.             jmp lExit
    54.         .endif
    55.          
    56.         ; Показать помощь
    57.         invoke lstrcmp, dword[buffer], comHelp
    58.         .if eax = 0
    59.             stdcall WriteLnStringToConsole, help1
    60.         .endif
    61.  
    62.         ; Загрузка файла
    63.         mov eax, [buffer]
    64.         .if byte[eax] = 'l'
    65.             stdcall LoadFile
    66.         .endif
    67.     .endw
    68.      
    69.  lExit:
    70.       ; Возвращаем память системе
    71.       .if dword[buffer] <> 0
    72.           invoke VirtualFree, [buffer], 4096, MEM_DECOMMIT      
    73.       .endif
    74.      
    75.       invoke ExitProcess, 0
    76.      
    77.  proc LoadFile uses eax ebx esi
    78.     invoke VirtualAlloc, 0, 512, MEM_COMMIT, PAGE_READWRITE     ; Резервируем место под заголовок PE
    79.     mov [bufferPE], eax
    80.     mov eax, [buffer]
    81.     add eax, 2
    82.     invoke CreateFile, eax, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
    83.     mov [handleFile], eax
    84.     .if eax = INVALID_HANDLE_VALUE
    85.         stdcall WriteLnStringToConsole, 'File not exit or accses denied'
    86.         ret
    87.     .endif
    88.      
    89.     invoke ReadFile, eax, [bufferPE], 512, temp, NULL
    90.      
    91.     ; Переходим к выводу содержимого PE построчно
    92.     mov eax, 0
    93.     mov esi, [bufferPE]
    94.     .while eax < 16
    95.         stdcall WriteToConsole16Bytes, esi
    96.         add esi, 16
    97.         add eax, 1
    98.     .endw
    99.    
    100.     ; Выводим содержимое EXEHEADER (00h...19h)    
    101.     stdcall WriteLnStringToConsole, 'EXEHEADER:'    
    102.     mov esi, [bufferPE]
    103.     mov ebx, @tableExeHeader - 1        ; В ebx теперь адрес таблицы для вывода
    104.     mov al, 0
    105.     .while TRUE
    106.         ; Выбираем символы до 0ffh
    107.         .while al <> 0ffh
    108.             add ebx, 1
    109.             mov al, byte[ebx]
    110.         .endw
    111.         .if byte[ebx+1] = 0ffh
    112.             jmp _exitCycle
    113.         .endif
    114.         add ebx, 2
    115.         stdcall WriteStringToConsole, ebx
    116.         stdcall SetXPosCursor, 19
    117.         movzx eax, byte[ebx-1]
    118.         stdcall WriteWordToConsole, dword[esi+eax]
    119.     .endw
    120.   _exitCycle:    
    121.  
    122.     invoke VirtualFree, [bufferPE], 512, MEM_DECOMMIT
    123.     ret
    124.     @tableExeHeader db 0ffh, 00h, 'Signature ', 0
    125.                     db 0ffh, 02h, 'Extra bytes ', 0
    126.                     db 0ffh, 04h, 'Pages ', 0
    127.                     db 0ffh, 06h, 'Relocation items ', 0
    128.                     db 0ffh, 08h, 'Header size ', 0
    129.                     db 0ffh, 0ah, 'Minimum allocation ', 0
    130.                     db 0ffh, 0ch, 'Maximum allocation ', 0
    131.                     db 0ffh, 0eh, 'Initialization SS ', 0
    132.                     db 0ffh, 10h, 'Initialization SP ', 0
    133.                     db 0ffh, 12h, 'Check Sum ', 0
    134.                     db 0ffh, 14h, 'Initialization IP ', 0
    135.                     db 0ffh, 16h, 'Initialization CS ', 0
    136.                     db 0ffh, 18h, 'Relocation table ', 0
    137.                     db 0ffh, 1ah, 'Overaly ', 0
    138.                     db 0ffh, 1ch, 'Reserved ', 0
    139.                     db 0ffh, 0ffh                      ; Признак конца таблицы
    140. endp
    141.  
    142. ; Установить горизонтальную позицию курсора
    143. proc SetXPosCursor uses eax, xPos:WORD
    144.     local info:CONSOLE_SCREEN_BUFFER_INFO
    145.     invoke GetConsoleScreenBufferInfo, [outputHandle], addr info
    146.     mov ax, [xPos]
    147.     mov [info.dwCursorPosition.x], ax
    148.     lea eax, [info.dwCursorPosition]
    149.     invoke SetConsoleCursorPosition, [outputHandle], dword[eax];
    150.     ret
    151. endp
    152.  
    153. ; Процедура выводит в консоль  16-битное значение
    154. proc WriteWordToConsole uses eax ebx edi, value
    155.     local buffer[6]:BYTE ; Буфер для полученной строки. 4 символа - столько занимает одно слово на экране  
    156.     lea edi, [buffer]    ; В edi адрес буфера для вывода
    157.     mov ebx, [value]
    158.     mov al, bh           ; В AL старший байт
    159.     stdcall ByteToString ; Преобразуем байт в строку
    160.     mov word[edi], ax    ; Сохраняем текстовое значене старшего байта
    161.     mov al, bl
    162.     stdcall ByteToString
    163.     mov word[edi+2], ax
    164.     mov word[edi+4], 0a0dh
    165.     invoke WriteConsole, [outputHandle], addr buffer, 6, temp, NULL
    166.     ret
    167. endp
    168.  
    169. ; Выводит строку в консоль. Пример вызова: stdcall WriteStringToConsole, 'String'
    170. proc WriteStringToConsole uses eax, addressString
    171.     stdcall LenString, [addressString]
    172.     invoke WriteConsole, [outputHandle], [addressString], eax, temp, NULL
    173.     ret
    174. endp
    175.  
    176. ; Выводит строку консоль и переводить курсор на следующую строку. Пример вызова: stdcall WriteLnStringToConsole, 'String'
    177. proc WriteLnStringToConsole, addressString
    178.     stdcall WriteStringToConsole, [addressString]
    179.     invoke WriteConsole, [outputHandle], @bufferEnter, 2, temp, NULL
    180.     ret
    181.     @bufferEnter db 0dh, 0ah
    182. endp
    183.  
    184. ; Находит длину строки
    185. proc LenString uses esi, addressString
    186.     mov esi, [addressString]    ; В esi теперь адрес строки, длину которой нужно вычислить
    187.     mov eax, 0
    188.     .while byte[esi+eax] <> 0
    189.         add eax, 1
    190.     .endw
    191.     ret
    192. endp
    193.  
    194. ; Процедура TetraToChar преобразует младщую тетраду регистра al в отображаемый 16-тиричный символ и возвращает в регистре al
    195. proc TetraToChar uses ebx
    196.     and al, 0fh     ; Обнуляем старшую тетраду
    197.     mov ebx, @table
    198.     xlat [ebx]
    199.     ret
    200.     @table db '0', '1', '2', '3', '4' ,'5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    201. endp
    202.  
    203. ; Процедура ByteToString преобразует содержимое регистра al в текстовое отображение (два символа) и возвращает их в регистре ax
    204. proc ByteToString uses ebx ecx
    205.     mov bl, al        ; Сохраняем содержимое регистра al в bl
    206.     ; Преобразуем старшую тетраду
    207.     shr al, 4         ; Для чего сдвигаем старшую тетраду в младшую
    208.     call TetraToChar  ; И преобразуем в символ
    209.     mov ch, al        ; И сохраняем в старшем байте cx
    210.     ; Теперь преобразуем младшую тетраду
    211.     mov al, bl
    212.     call TetraToChar
    213.     mov cl, al        ; И сохраняем в младшем байте cx
    214.     mov ax, cx      ; Возвращаем результат
    215.     xchg al, ah
    216.     ret
    217. endp
    218.  
    219. ; Процедура WriteToConsole16Bytes выводит в консоль 16 байт, начиная с startAddress
    220.     proc WriteToConsole16Bytes uses eax ecx esi edi, startAddress
    221.     local buffer[50]:BYTE
    222.     mov ecx, 0
    223.     mov esi, [startAddress]   ; Теперь в esi находится адрес первого байта
    224.     lea edi, [buffer]         ; А в edi адрес итоговой строки - той, которую будем выводить на экран
    225.     .while ecx < 16
    226.        mov al, [esi]          ; Загружаем очередной байт в al
    227.        call ByteToString      ; Преобразуем его в текстовый вид
    228.        mov word[edi], ax      ; Сохраняем в буфере для вывода
    229.        add edi, 2
    230.        mov byte[edi], ' '     ; И записываем за ним пробел
    231.        add edi, 1             ; Переходим к
    232.        add esi, 1             ; следующему байту
    233.        add ecx, 1
    234.     .endw
    235.     invoke WriteConsole, [outputHandle], addr buffer, 50, temp, NULL
    236.     ; А теперь выводим в текстовом виде
    237.     mov esi, [startAddress]
    238.     lea edi, [buffer]
    239.     mov ecx, 0
    240.     .while ecx < 16
    241.        mov al, byte[esi]
    242.        .if al < ' '
    243.            mov al, '.'
    244.        .elseif al = 255
    245.            mov al, '.'
    246.        .endif
    247.        mov byte[edi], al
    248.        add esi, 1
    249.        add edi, 1
    250.        add ecx, 1
    251.     .endw
    252.     mov byte[edi], 0dh
    253.     add edi, 1
    254.     mov byte[edi], 0ah
    255.     invoke WriteConsole, [outputHandle], addr buffer, 18, temp, NULL
    256.     ret
    257. endp
    258.  
    259. ; Процедура ReadStringFromConsole читает строку символов с консоли и сохраняет её по заданному адресу. numBytes - максимальное число вводимых символов
    260. ; Символ ввода заменяется 0 - текстовая строка превращается в С++-строку
    261. proc ReadStringFromConsole uses esi, address, numBytes
    262.     local realBytes:DWORD
    263.     invoke ReadConsole, [inputHandle], [address], [numBytes], addr realBytes, 0        ; Читаем команду с консоли
    264.     mov esi, [address]      ; В esi адрес первого байта полученной строки
    265.     sub esi, 1
    266.     .repeat
    267.        add esi, 1
    268.     .until byte[esi] = 0dh
    269.     mov byte[esi], 0        ; И заменяем его на 0
    270.     ret
    271. endp
    272.    
    273. ;**********************************************************************************************
    274. section '.data' data readable writeable
    275.    temp dd ?
    276.    outputHandle dd ?
    277.    inputHandle dd ?
    278.    buffer dd 0          ; Здесь указатель на выделяемый буфер под вводимую строку
    279.    bufferPE dd 0    ; Здесь будет указатель на область памяти, в которой будет заголовок PE
    280.    handleFile dd 0
    281.  
    282.    ; Дальше строки. Некоторые даже с длинами
    283.    
    284.    ; Это строки главного меню
    285.    comHelp     db '?', 0
    286.    comLoadFile db 'l', 0
    287.    comExit     db 'q', 0
    288.    
    289.    ; Это строки, которые выводятся по меню "Help"
    290.    help1 db 'help        ? - this screen', 0dh, 0ah
    291.    help2 db 'load <file> l - loading <file>', 0dh, 0ah
    292.    help3 db 'quit        q - not comments', 0
    293.    
    294. ;***************************************************************************************************
    295. section '.idata' import data readable writeable
    296.    library kernel, 'KERNEL32.DLL'
    297.  
    298.    import kernel,\
    299.       ExitProcess, 'ExitProcess',\
    300.       GetStdHandle, 'GetStdHandle',\
    301.       WriteConsole, 'WriteConsoleA',\
    302.       ReadConsole, 'ReadConsoleA',\
    303.       lstrcmp, 'lstrcmp', \
    304.       VirtualAlloc, 'VirtualAlloc',\
    305.       VirtualFree, 'VirtualFree',\
    306.       CreateFile, 'CreateFileA',\
    307.       ReadFile, 'ReadFile',\
    308.       GetConsoleScreenBufferInfo, 'GetConsoleScreenBufferInfo',\
    309.       SetConsoleCursorPosition, 'SetConsoleCursorPosition'
    Прошу совета знающих людей. Посмотрите код, пожалуйста, скажите, достаточно ли он читабелен, что в нём стоит упростить, что наоборот, усложнить.
    Самому не очень нравится.
    Хотелось бы побольше всяких советов)
     
  2. JCronuz

    JCronuz New Member

    Публикаций:
    0
    Регистрация:
    26 сен 2007
    Сообщения:
    1.240
    Адрес:
    Russia
    Главным критерием всегда был и остается правильность/работоспособность, это мое имхо. Если все работает, то зачем нужно менять, улучшать?
     
  3. MSoft

    MSoft New Member

    Публикаций:
    0
    Регистрация:
    16 дек 2006
    Сообщения:
    2.854
    я не спец, но мне понравилось
     
  4. cupuyc

    cupuyc New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2009
    Сообщения:
    763
    х.з. не понимаю зачем писать на асме .if .while и пр. пиши уж тогда на си - он хоть оптимизировать будет твои макросы. это моё субъективное имхо: если асм - то чистый, безо всяких там .if, если нужно писать большой проект и сделать его читабельным, то пиши на сях.
     
  5. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    cupuyc
    как можно не согласиться :derisive: да и printf решает. А сам код очень даже стройный, вроде с первого взгляда все видно.
     
  6. cupuyc

    cupuyc New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2009
    Сообщения:
    763
    да я не про то, что проще, а про то, как правильнее имхо. мой субъективный взгляд говорит мне, что правильнее подобные задачи решать на сях. если же решать их на асме то без использования всяких ифов и пр. в противном случае я не вижу толку от использования асма. могу и ошибаться, не спорю.
     
  7. Phuntik

    Phuntik New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2008
    Сообщения:
    318
    JCronuz
    Это верно лишь отчасти. На работе одна чувиха тоже так говорит. Потом позвала меня : "Посмотри, где у меня ошибка". Я глянул, а там - button1, var1, никакого форматирования, бегины не соответсвтуют ендам. Совершенно никакой системы в написании. Я ей честно сказал, что копаться в ЭТОМ у меня нет никакого желания. Она мне в ответ сказала "Главное - чтоб работало". Естественно было ответить - "Ну так у тебя и не работает". Но я сдержался. За это мне +1.
    А суть моего вопроса в том, чтобы код сделать как можно более читабельным. Например, в первом варианте этой программы не было подпрограммы для вывода строки. Из главной функции вызывалась функция WriteConsole и туда передавались параметры. Для улучшения наглядности была сделана WriteStringToConsole с одним параметром. Так проще код читается, никто не будет спорить.
    Далее. Вначате не было условных операторов типа .if. В итоге по всему коду были разбросаны метки. От этого он закакался и выглядел сложным для восприятия. После добавления высокоуровневых конструкций он стал более простым. Да, после этого код стал не чисто ассемблерным, и, как показал отладчик, генерируемые fasm-ом инструкции не совершненны. Но код перестал быть чисто ассемблерным, когда я стал в нём применять invoke, а наглядность получаемого покрывает второй недостаток.

    MSoft
    Спасибо)

    cupuyc
    Основная причина - код выглядит очень сложным.

    Полностью согласен. Лучше писать на том, на чем будет быстрее. Если брать мой случай, то я пишу не для практических целей, а скорее для учебных. Умею и без ифов, значит, могу их использовать. Если подскажешь, что можно простого написать кроме просмотрщика заголовка исполняемого файла, буду весьма мерси.

    А вообще, акромЯ первых двух ответов, ничего позитивного. Вопрос был в том, как сделать данный код более совершенным с точки зрения чтения. Ёлы-палы, никто даже не поинтересовался, почему у меня вместо inc add используется.
    Вопрос открыт. Если кто-то увидит, что здесь можно улучшить, буду весьма признателен.
     
  8. Phuntik

    Phuntik New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2008
    Сообщения:
    318
    SPA
    Спасибо.
     
  9. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    Phuntik
    так ты скажи ЗАЧЕМ? я бы посмотрел, а так тут ответ просто переходи на си, а если для учебных целей, то почему add вместо inc?
     
  10. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    Глянул еще раз, ну все понятно, но и код достаточно простой ведь. Удачная структура ( ну на первый взгляд хотя бы) tableExeHeader
     
  11. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    add быстрее :)
     
  12. spa

    spa Active Member

    Публикаций:
    0
    Регистрация:
    9 мар 2005
    Сообщения:
    2.240
    n0name
    у нас критерий "читабельность" тогда inc рулит, хотя хз ))) add eax, 1 тоже не сложно
     
  13. cupuyc

    cupuyc New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2009
    Сообщения:
    763
    что-то я начинаю сомневаться вообще во всём. какого лешего было вводить инструкцию inc, если есть add и она быстрее? вобщем я асма не знаю вообще :dntknw:(((
     
  14. Clerk

    Clerk Забанен

    Публикаций:
    0
    Регистрация:
    4 янв 2008
    Сообщения:
    6.689
    Адрес:
    РБ, Могилёв
    В юзермоде не имеет значения сложение или инкремент, ибо планирование выполняется, темболее что не миллионы итераций.
    Вобщем код оформлен нормально. Из недостатков - сразу заметил не приятные сравнения регистров с нулём.
     
  15. KeSqueer

    KeSqueer Сергей

    Публикаций:
    0
    Регистрация:
    19 июл 2007
    Сообщения:
    1.183
    Адрес:
    Москва
    Вместо .if eax = 0 поставить .if ~eax
     
  16. Black_mirror

    Black_mirror Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2002
    Сообщения:
    1.035
    Phuntik
    На мой взгляд в программе излишнее злоупотребление функцией VirtualAlloc, потому что выделяются очень небольшие буферы фиксированной длины. Лучше зарезервировать для них место в конце секции данных.
    Очень неэффективно написаны функции XXXToString. Лучше написать одну функцию которая принимает в eax число, а в ecx сколько цифр выводить, а вместо XXXToString написать макросы.
     
  17. Phuntik

    Phuntik New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2008
    Сообщения:
    318
    SPA
    Чем проще разобраться в коде, тем проще над ним работать. Вот и стремлюсь к совершенству. Конечно, это приходит с опытом, но также я не против поучиться у других.
    add вместо inc действительно, потому как считал из какого-то источника, что он быстрее. Хотя сомнительно весьма - копилятор VS никогда не делает add 1, а всегда inc.
    А что касается того, что inc нагляднее, то есть такое правило - "Преждевременная оптимизация - это плохо, но какие-то вещи нужно оптимизировать автоматически". Например, в этом случае:
    for(int i = 0; i < widht(); i++) {
    }
    нужно писать так:
    int w = widht();
    for(int i = 0; i < w; i++) {
    }

    Black_mirror
    Да, в первой версии динамического выделения памяти не было, добавил его позднее, так как не совсем правильным показалось, что данные занимают на порядок больше места, чем код). Потом, очевидно, уберу.
    Да, это достаточно тёмный момент. Понятно, что надо улучшать. Я и сам в них путаюсь.

    В-общем, будем считать, что код достаточно хорошо и читабельно написан. Я и обратился потому, что не так красиво, как на С, получается. Но нагляднее, наверное, и не получится)
     
  18. J0E

    J0E New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2008
    Сообщения:
    621
    Адрес:
    Panama
    Качество кода можно оценить по количеству "what the fuck?" в минуту.
    Поехали:
    - outputHandle и другие глобальные переменные зачем нужны? уж точно не файловый хендл.
    - что из себя представляет конструкции .if eax = 0 : jmp lExit - это 2 джампа подряд?
    - что еще делает LoadFile кроме чтения файла, вывод на консоль что ли? ) кстати почитай про file mapping и SEC_IMAGE.
    - смешивать код и данные плохо. если есть необходимость, располагай ud2 после ret, по рекомендации Интел.
     
  19. Phuntik

    Phuntik New Member

    Публикаций:
    0
    Регистрация:
    4 фев 2008
    Сообщения:
    318
    J0E
    О! Серьёзный подход. И сразу видно, что всё не так уж хорошо. Что ж, рассмотрим по порядку.
    Насчёт outputHandle(хэндл консоли) уж точно. Лучше бы было всё это запрятать в одну функцию вывода в консоль и там уж руководить. По мере разрастания программы это и произойдёт, видимо. Вообще, в программе на ассемблере глобальные переменные - это совсем неплохо, разве не так? По крайней мере, пока их не много и не составляет труда следить за ними.
    Остальные глобальные переменные - строки для вывода в основном. И где ж их хранить?
    Ну уж файловый хэндл не набо было оставлять на виду, это точно.
    Ну, сам компилятор совсем не здорово генерирует команды для .if. Ничего страшного, если я ещё добавил неоптимальности. Оптимальность принесена в жертву читабельности, что в данном случае вполне оправданно. Если этот участок будет выполняться в цикле и нужна будет скорость, я, скорее всего, перепишу его без использования .if
    Да, вывод на консоль. Название неудачное, согласен.
    Поподробнее можно? Данные я располагаю или в стеке, или после ret. Что касается передачи строк в функции, то действительно, очень страшный код в отладчике получается, но я не знаю другого способа, кроме как вручную размещать строки в памяти и передавать их адреса, что жутко неудобно. Если знаешь третий способ, то поделись.
    И что такое ud2, и поделись ссылочкой на рекомендацию Интел, пожалуйста, можно просто номер главы и тома)
     
  20. cupuyc

    cupuyc New Member

    Публикаций:
    0
    Регистрация:
    2 апр 2009
    Сообщения:
    763
    как-то один знакомый сказал, что некрасивый код не может быть рабочим.