Несколько технических вопросов по ассемблеру

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

  1. net_name

    net_name New Member

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

    1) Набросал крохотную програмку:
    Код (Text):
    1. .386
    2. .model flat, stdcall
    3.  
    4. option casemap: none
    5.  
    6. include C:\masm32\include\windows.inc
    7. include C:\masm32\include\kernel32.inc
    8. include C:\masm32\include\user32.inc
    9. include C:\masm32\include\advapi32.inc
    10.  
    11. includelib C:\masm32\lib\kernel32.lib
    12. includelib C:\masm32\lib\user32.lib
    13. includelib C:\masm32\lib\advapi32.lib
    14.  
    15. .data
    16.  
    17. StrPirat    DB      "PRIVET", 0
    18.  
    19. .code
    20. START:
    21.     push 0
    22.     push offset StrPirat
    23.     push offset StrPirat
    24.     push 0
    25.     call MessageBox
    26.  
    27. EXIT:
    28.     push 0
    29.     call ExitProcess
    30.  
    31. END START
    Транслировал так: ml.exe /coff /c source.asm
    Линковал сначала так link.exe /SUBSYSTEM:WINDOWS source.obj
    Файл получается 2,5 Кб
    Потом добавил ключ выравнивания, который использую при написании на С
    link.exe /ALIGN:32 /SUBSYSTEM:WINDOWS source.obj
    Получается 800 байт

    И вот у меня возникает вопрос, это что максимум оптимизации? Просто так можно было и на С написать, изменить WinMain скажем на _WinMain добавить ключи выравнивания результат такой же. Вообще я предполагал, что что-то должно получаться скажем ~100 байт или меньше, ведь прога нечего не делает... Но с другой стороны предполагаю что это связано со структурой PE файла? Это так?

    2) Открываю полученный экзешник в Hiew32.exe перехожу в режим Decode (F4) как я понимаю это режим дизассемблирования, так?

    3) Hiew32 слева колонку с .00400000 по .0040031E это адреса памяти?
    То есть .00400000 это какой то базовый адрес?

    4) Тогда если следовать логике .0040031E - .00400000 = 31E или 798 байт, однако файл 800 байт ровно, где же ещё 2 байта?

    5) Почему Hiew32.exe печатает практически везде (странные машинные инструкци) что-то типо:
    Код (Text):
    1. ▓.004000AC: 0000                           add         [eax],al
    2. ▓.004000AE: 0000                           add         [eax],al
    3. ▓.004000B0: 50                             push        eax
    4. ▓.004000B1: 45                             inc         ebp
    5. ▓.004000B2: 0000                           add         [eax],al
    6. ▓.004000B4: 4C                             dec         esp
    7. ░.004000B5: 0103                           add         [ebx],eax
    8. ░.004000B7: 007887                         add         [eax][-079],bh
    9. ░.004000BA: 9F                             lahf
    10. ░.004000BB: 4A                             dec         edx
    11. ░.004000BC: 0000                           add         [eax],al
    12. ░.004000BE: 0000                           add         [eax],al
    13. ░.004000C0: 0000                           add         [eax],al
    14. ░.004000C2: 0000                           add         [eax],al
    15. ░.004000C4: E000                           loopne     .0004000C6 --↓1
    16. ░.004000C6: 0F010B                        1sidt        [ebx]
    17. ░.004000C9: 01050C400000                   add         [00000400C],eax
    18. ░.004000CF: 00C0                           add         al,al
    19. ░.004000D1: 0000                           add         [eax],al
    20. ░.004000D3: 0000                           add         [eax],al
    21. ░.004000D5: 0000                           add         [eax],al
    Только ближе к концу появляется действительно что-то похожее на то, что было запрограммировано:
    Код (Text):
    1. .00400220: 6A00                           push        0
    2. .00400222: 6804034000                     push        000400304 ;'PRIVET' --↓1
    3. .00400227: 6804034000                     push        000400304 ;'PRIVET' --↓1
    4. .0040022C: 6A00                           push        0
    5. .0040022E: E80D000000                     call        MessageBoxA --↓2
    6. .00400233: 6A00                           push        0
    7. .00400235: E800000000                     call        ExitProcess --↓3
    8. .0040023A: FF2560024000                  3jmp         ExitProcess
    9. .00400240: FF2568024000                  2jmp         MessageBoxA
    Вот я и не пойму, по идеи только это должно быть кодом и располагаться в секции кода, а что же тогда всё остальное? Мусор? :)

    6) Попробовал открыть файл в OllyDbg. Надо сказать вот эта прога понравилась, никаких линих инструкций не писала... :)
    Сразу вывела то, что надо (сокращёно):
    Код (Text):
    1. 0040022E  |.  E8 0D000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
    2. 00400233  |.  6A 00         PUSH 0                                   ; /ExitCode = 0
    3. 00400235  \.  E8 00000000   CALL <JMP.&kernel32.ExitProcess>         ; \ExitProcess
    4. 0040023A   .- FF25 60024000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>;  kernel32.ExitProcess
    5. 00400240   $- FF25 68024000 JMP DWORD PTR DS:[<&user32.MessageBoxA>] ;  user32.MessageBoxA
    6. 00400246      00            DB 00
    7.  
    8. здесь -> 00400XXX      00            DB 00
    9.  
    10. 0040025F      00            DB 00
    11. 00400260 > .  12CB817C      DD kernel32.ExitProcess
    12. 00400264      00000000      DD 00000000
    13. 00400268 > .  EA073A7E      DD user32.MessageBoxA
    14. 0040026C      00000000      DD 00000000
    15. 00400270   .  AC020000      DD 000002AC                              ;  Struct 'IMAGE_IMPORT_DESCRIPTOR'
    16. 00400274   .  00000000      DD 00000000
    17. 00400278   .  00000000      DD 00000000
    18. 0040027C   .  CA020000      DD 000002CA
    19. 00400280   .  60020000      DD 00000260
    20. 00400284   .  B4020000      DD 000002B4                              ;  Struct 'IMAGE_IMPORT_DESCRIPTOR'
    21. 00400288   .  00000000      DD 00000000
    22. 0040028C   .  00000000      DD 00000000
    23. 00400290   .  E6020000      DD 000002E6
    24. 00400294   .  68020000      DD 00000268
    25. 00400298   .  00000000      DD 00000000                              ;  Struct 'IMAGE_IMPORT_DESCRIPTOR'
    26. 0040029C   .  00000000      DD 00000000
    27. 004002A0   .  00000000      DD 00000000
    28. 004002A4   .  00000000      DD 00000000
    29. 004002A8   .  00000000      DD 00000000
    30. 004002AC   .  BC020000      DD 000002BC                              ;  Import lookup table for 'kernel32.dll'
    31. 004002B0   .  00000000      DD 00000000
    32. 004002B4   .  D8020000      DD 000002D8                              ;  Import lookup table for 'user32.dll'
    33. 004002B8   .  00000000      DD 00000000
    34. 004002BC   .  9B00          DW 009B
    35. 004002BE   .  45 78 69 74 5>ASCII "ExitProcess",0
    36. 004002CA   .  6B 65 72 6E 6>ASCII "kernel32.dll",0
    37. 004002D7      00            DB 00
    38. 004002D8   .  B101          DW 01B1
    39. 004002DA   .  4D 65 73 73 6>ASCII "MessageBoxA",0
    40. 004002E6   .  75 73 65 72 3>ASCII "user32.dll",0
    41. 004002F1      00            DB 00
    42.  
    43. здесь -> 00400XXX      00            DB 00
    44.  
    45. 00400303      00            DB 00
    46. 00400304   .  50 52 49 56 4>ASCII "PRIVET",0
    47. 0040030B      00            DB 00
    48.  
    49. здесь -> 00400XXX      00            DB 00
    50.  
    51. 00400FFF      00            DB 00
    7) Олька распечатала всё с .00400220 по .00400FFF, таким образом разница составляет 3551 байт, вот это я вообще не понял...

    8) Вообще что значит 00400XXX 00 DB 00 в плане дизассемблера? Что это мусор?

    9) И последний вопрос по коду:
    Код (Text):
    1. 0040023A   .- FF25 60024000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>;  kernel32.ExitProcess
    2. 00400240   $- FF25 68024000 JMP DWORD PTR DS:[<&user32.MessageBoxA>] ;  user32.MessageBoxA
    Вопрос вызывает регистр DS. Что означает эта строка?
    Просто раньше в С я иногда вставлял ассемдлерные вставки, однако чтобы функция коректно работала надо было писать так:
    Код (Text):
    1. #include "stdafx.h"
    2. #include "windows.h"
    3.  
    4. int _tmain(int argc, _TCHAR* argv[])
    5. {
    6.     char * text = "TEXT";
    7.     char * title = "TITLE";
    8.     __asm
    9.     {
    10.         push 0;
    11.         push title;
    12.         push text;
    13.         push 0;
    14.  
    15.         call ds:MessageBox;
    16.     }
    17.     return 0;
    18. }
    Зачем нужно принудительно указывать DS? (call ds:MessageBox) Как это связано с дизассемблерным листингом? Раньше этому значения не придавал теперь хочу понять :)

    Буду очень признателен всем за любую помощь... Спасибо :)
     
  2. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    net_name
    Давай попробуем написать твою программу еще короче
    1) вместо push 0/ call ExitProcess ставим retn
    и соответственно убираем строки "include C:\masm32\include\kernel32.inc
    include C:\masm32\include\advapi32.inc
    includelib C:\masm32\lib\kernel32.lib
    includelib C:\masm32\lib\advapi32.lib"
    компилируем, линкуем, запусаем. Работает? А теперь смотрим на размер -- стала ли программа меньше?
    2) уменьшаем выравнивнивание /ALIGN:16
    компилируй, линкуй, запускаем. Работает? А размер?
    3) помещаем данные в секцию кода не забудь добавить "link.exe /MERGE:.data=.text"
    заодно избавляемся от ненужного jmp MessageBox
    Код (Text):
    1. .686P
    2. .model flat, stdcall
    3. option casemap: none
    4. include windows.inc
    5. includelib user32.lib
    6. extern _imp__MessageBoxA@16:dword
    7. .code
    8. start:  push eax;MB_OK
    9.     mov edi,offset StrPirat
    10.     push edi
    11.     push edi
    12.     push eax
    13.     call _imp__MessageBoxA@16
    14.     ret
    15. StrPirat      db "PRIVET",0
    16. end start
    компилируем, линкуем, запускаем. Работает? Изменился ли размер?
    А дальше, чтобы не повторяться загляни сюда
     
  3. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Mikl___
    Спасибо тебе за помощь... :) Конечно всё здорово уменьшилось...
    Но ты как бы помог решить проьлему вопрос в сути...
    Если по порядку...
    Вот мне интересно каким образом правильно устанавливать это выравнивание, что оно означает, как это понять...
    Как его правильно определять если оно можно менять?
    А хранить данные вместе с кодом это вообще хороший тон? Это безопасно?
    Я не очень понял от какого jmp'а мы избавились?

    А это правильно, что ты первым параметром в MessageBox передаём eax в котором MB_OK? Там вроде хендл должен...
     
  4. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    И кстати ret в асме работает также как и в С?
     
  5. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    С eax разобрался, просто тупанул :)
    Хотя можно было и занулить...
     
  6. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    3.708
    net_name
    1) Для Win98 выравнивание 512, для WinXP можно даже 4
    2) "хранить данные вместе с кодом это вообще хороший тон? Это безопасно?" главное, чтобы на данные не было передано управление, поэтому они и размещены за инструкцией retn, хотя можно как у тебя поместить перед меткой start
    3) "кстати ret в асме работает также как и в С?" а как ret работает в С? В ассемблере это 4 различных команды с разной кодировкой retn (0xC3), retf (0xCB), retn XX (0xC2 XX), retf XX (0xCA XX)
    4) В WinXP при старте eax=0 push eax - 1 байт, а push 0 - 2 байта
     
  7. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Mikl___
    1) Всё таки по выравниванию нужно почитать... Что это такое и от чего зависит... Вот только где бы найти... Попробую погуглить...
    2)
    Так ведь данные всё равно будут использоваться в тексте программы, как же на них не может быть переданно управление? Если ты скажем объявил переменную, а потом передаёшь в функцию...

    Или это как в С с прототипами функций, мол функцию нельзя юзать если не объявил её или её прототип до использования в программе?

    3) Как раз это и имею ввиду... Там просто return, а здесь наверное "игры со стеком" :)
    поэтому и 4 различных команды с разной кодировкой retn (0xC3), retf (0xCB), retn XX (0xC2 XX), retf XX (0xCA XX) так?
    4) Ну не обязательно же запускать под WinXP, но это запомню...
     
  8. blueboar

    blueboar New Member

    Публикаций:
    0
    Регистрация:
    29 авг 2004
    Сообщения:
    110
    Адрес:
    Россия, Курган
    Да. По умолчанию все программы в Windows с него начинаются. Но его легко можно заменить ключом компиляции

    Если следовать вашей логике, то если бы программа начиналась и заканчивалась по одному адресу 400000, то у вас она была бы 0 байт?! У меня - один.

    Потому что вначале идет MZ заголовок, затем PE заголовок, затем описания секций. Естественно это не код. Но HIEW его показывает. Мало ли - вдруг вы и их захотите поменять. Так что это не недостаток HIEW, а его достоинство.

    Правильно, она просто вам заголовки не показала, а только секцию кода

    Да, мусор.
     
  9. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Спасибо за помощь blueboar :)

    ImageBase ведь это адрес не в вуртуальной памяти?

    Ещё где можно прочитать как система грузит exe. И как устроена память. То есть системный загрузчик читает из PE заголовка информацию, и что потом... В какую именно память грузит? Как её выделяет? Если по умолчанию для ,большинства экзешников ImageBase=.00400000 значит для всех экзешников сначала создаётся своё виртуальное пространство, а потом там идёт расположение экзешника. Как это связано с физической памятью... Вот нужно что-то по архитектуре...

    blueboar я не очень понял, что Вы имели ввиду... Начинается с базового адреса .00400000 и заканчивается на адресе .0040031E значит если из большего ареса вычесть меньший получим полный размер...

    То есть он видит двоичные данные и пытается интепретировать соответствующие опкоды машинных команд в понятное (для человека) представление, даже если это не код?

    Но уже много становится понятным и чем дальше, тем сильнее интереснее!!! Узнаёшь вещей больше, чем в С и очень интересно :)
     
  10. saiNT

    saiNT New Member

    Публикаций:
    0
    Регистрация:
    22 авг 2009
    Сообщения:
    6
    http://www.wasm.ru/print.php?article=packlast01
    http://www.wasm.ru/article.php?article=packers2

    Да, ибо узнать код это или данные практически невозможно (где-то даже на этом форуме топ был по этому вопросу)

    Полуим размер виртуальной памяти, отведенной под секцию.
     
  11. saiNT

    saiNT New Member

    Публикаций:
    0
    Регистрация:
    22 авг 2009
    Сообщения:
    6
    http://www.wasm.ru/article.php?article=packlast01 (эта рабочая ссылка)

    p.s.
    мммм, я новичек на форуме, редактирования постов нет или я не нашел?
     
  12. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Бъёшь в десятку таже фигня сегодня искал-искал редактирование - не нашёл...
     
  13. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Сейчас почитаю твои ссылки... Спасибо saiNT :)
     
  14. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    П.С.: по поводу регистра DS вопрос ещё открыт :)
     
  15. blueboar

    blueboar New Member

    Публикаций:
    0
    Регистрация:
    29 авг 2004
    Сообщения:
    110
    Адрес:
    Россия, Курган
    Не совсем. Если программа начинается по адресу 40000, а заканчивается 40001, то по вашей логике она занимает 40001-40000=1 байт. А на самом деле два - 40000 и 40001.

    А разница в два байта у вас получилась скорее всего потому, что по адресу 40031E была команда размером в два байта - она занимала адреса 40031E и 40031F
     
  16. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    А всё, теперь понятно :) К слову там стоит add [eax], al
    Спасибо blueboar
     
  17. saiNT

    saiNT New Member

    Публикаций:
    0
    Регистрация:
    22 авг 2009
    Сообщения:
    6
    Это пустой пространство - нули имеют такое представление ;)
    0000 add [eax], al
    0000 add [eax], al
    ...
     
  18. saiNT

    saiNT New Member

    Публикаций:
    0
    Регистрация:
    22 авг 2009
    Сообщения:
    6
    Такова реализация вызова API функции у С, у делфей по-другому, на масме по-третьему. Думаю не стоит на отом заморачиваться.
     
  19. net_name

    net_name New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2009
    Сообщения:
    25
    Просто я заморочился потому что не понятно что в OllyDbg написано:
    Код (Text):
    1. 0040022E  |.  E8 0D000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA
    2. 00400233  |.  6A 00         PUSH 0                                   ; /ExitCode = 0
    3. 00400235  \.  E8 00000000   CALL <JMP.&kernel32.ExitProcess>         ; \ExitProcess
    4. 0040023A   .- FF25 60024000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>;  kernel32.ExitProcess
    5. 00400240   $- FF25 68024000 JMP DWORD PTR DS:[<&user32.MessageBoxA>] ;  user32.MessageBoxA
    6. 00400246      00            DB 00
    Что значит эти две строки?
    0040023A .- FF25 60024000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>; kernel32.ExitProcess
    00400240 $- FF25 68024000 JMP DWORD PTR DS:[<&user32.MessageBoxA>] ; user32.MessageBoxA
     
  20. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    а это тот джамп про который вам Mikl___ написал во втором посте