Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Assembler

Страница: 1 | 2 |

 

  Вопрос: Почему обнуляется? Добавлено: 22.09.04 03:33  

Автор вопроса:  CyRax  | Web-сайт: basicproduction.nm.ru | ICQ: 204447456 

Ответить

  Ответы Всего ответов: 29  

Номер ответа: 16
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #16
Добавлено: 23.09.04 02:56
Допустим процедура начинается так
Push EBP
Mov EBP,ESP
Push EBX
Push ESI
Push EDI

Теперь разберу:
Mov EBP,ESP <- Приравниваем базу указателю.
Push EBX
Push ESI
Push EDI <- ESP изменился? Нужно длину этих трёх регистров учитывать при объявлении локальных переменных?

Теперь моё понимание SUB ESP,imm
При дизассемблировании программ на PB/WIN заметил что эта операция выполняется один раз в начале процедуры.
А раз мы приравняли базу текущему адресу стека, получается что ESP после вычитания из него будет указывать на адрес перед EBP. В чём отличие от того что он был после EBP? И нужно ли возвращать каждый раз ESP на место перед выполнением PUSH?

Ответить

Номер ответа: 17
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #17 Добавлено: 23.09.04 04:48
Нет, выбивало потому что обнуляло


Ну так ты пытался считать\записать где не положено, доступа нет, потому и не запишешь, ноль возвращался и Call 0 - ошибка

О eip. Он программно недоступен, но можно сделать финт ушами:
    Call EIPLabel ;eip - в стек
EIPLabel:
    pop eax ;здесь в eax будет адрес метки EIPLabel( он же текущий адрес)

Ошибку с библой разобрался. Затер случайно в обработке DLL_PROCESS_ATTACH возврат return TRUE, ну и получается, что без TRUE, как будто произошла ошибка (по ошибке надо возвращать FALSE). Вот система и думала, что ошибка.

Нужно длину этих трёх регистров учитывать при объявлении локальных переменных?


Вот смотри, такая процедура:
RestoreBackUp proc
LOCAL hKey :DWORD
параметров нет, локальных одна переменная. В отладчике код:
    Push EBP
    Mov EBP,ESP
    add esp,-4 ;тоже, что и sub esp,4

Вот другая процедура:
CreateBackUp proc
LOCAL hKey :DWORD
LOCAL cbSize :DWORD
    mov cbSize,256
параметров снова нет, локальных - уже две. В отладчике код:
    Push EBP
    Mov EBP,ESP
    add esp,-8 ;тоже, что и sub esp,8
    mov dword ptr [ebp-8],100h ;записал в cbSize 256, т.е. первая локальная -4, вторая -8

теперь перед mov cbSize,256 вставляю push esi, снова компилю и в отладчике mov dword ptr [ebp-8],100h не изменилось. Получается, что если пушишь какие-нибудь регистры, то в стеке может быть и нарушается адресация, но мы то переменные адресуем не по esp, а по ebp. Т.е. при входе сразу же сохранили состояние вершины в ebp, и что бы мы потом не делали с esp (пушили или попили:)) переменные мы адресуем по первоначальному esp, который у нас сохранился в ebp. Поэтому от манипуляций со стеком адреса не теряются.

А вот третья процедура:
ReplaceButton proc hDlg:dword, ButtIndex:dword, NewFolderAddr:dword, IconFlag:dword, IconNameAddr:dword

параметров 5, локальных нет.

код в отладчике:
    Push EBP
    Mov EBP,ESP
все. в минус esp не лезет. параметр hDlg передаётся в другую проц. как [ebp+8], ButtIndex - как [ebp+0C], NewFolderAddr - [ebp+10h], и т.д.

Из этих трёх вызовов разных процедур наверное можно так утверждать: эти три регистра (вернее их занесение в стек) на локальные и на параметры не влияют. Но это будет справедливо, если перед тем как начнёшь манипулировать со стеком, выполнишь комбинацию
    Push EBP
    Mov EBP,ESP

Опять же это всё имхо. Хотя вроде код рассмотренный в отладчике, подтверждает это.

В чём отличие от того что он был после EBP? И нужно ли возвращать каждый раз ESP на место перед выполнением PUSH?


Тут я не понял сути вопроса :(

Ответить

Номер ответа: 18
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #18
Добавлено: 23.09.04 06:08
Вот описание команды PUSH

Decrements SP by the size of the operand (two or four, byte values are sign extended) and transfers one word from source to the stack

По моему до меня дошло. Он же вычитает из ESP при PUSH! Это значит что затереть он ничего не может.

Вот оказывается чего я так путался. Я думал что стек работает совсем наоборот. Ну что при складировании ESP увеличивается. А оно вон как.
 Теперь буду знать. Примерно это работает так:
 ESP=EBP
 PUSH <- ESP = ESP-4 = EBP-4
 PUSH <- ESP = ESP-4 = EBP-8
 PUSH <- ESP = ESP-4 = EBP-16
 ...
 POP <- ESP = ESP+4 = EBP-8
 POP <- ESP = ESP+4 = EBP-4
 POP <- ESP = ESP+4 = EBP
 ESP=EBP

Ответить

Номер ответа: 19
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #19
Добавлено: 23.09.04 06:19
Ну так ты пытался считать\записать где не положено, доступа нет, потому и не запишешь, ноль возвращался и Call 0 - ошибка

 Ты не прав. Нормально он всё записывал. Только после стековых команд оно обнулялось. Может быть даже я сам его затирал. Не веришь? Смотри:
 PUSH 0 ; MB_OK | ESP=[EBP-4]
 PUSH Title ; ESP=[EBP-8]
 PUSH Caption ; ESP=[EBP-12]
 PUSH 0 ; hWnd | ESP=[EBP-16]
 CALL [EBP-16] ; CALL 0 :)

Ответить

Номер ответа: 20
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #20
Добавлено: 23.09.04 06:40
Ты понял к чему я клоню?
 SUB ESP,8
это тоже самое что и
 PUSH r/m/imm32
 PUSH r/m/imm32
только предыдущие их значения не затираются новыми.

Ты можешь первое заменить на второе и это будет верно. Вобщем устройство стека я думаю мы с тобой поняли :)

Ответить

Номер ответа: 21
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #21 Добавлено: 23.09.04 07:44
Ну дык он всегда и рос в сторону меньших адресов. По определению в сторону больших адресов расположены входные параметры процедуры, и стек в ту сторону расти не имеет права, чтобы не затирать параметры. По смещению +4 и 0 расположены адрес возврата и сохраненный ebp. Остается только в сторону меньших адресов.

А CALL [EBP-16] ; CALL 0 - по моему, я понял, в чем проблема, ты не выдели место под локальные и сразу начал пушить регистры:

Push EBP
Mov EBP,ESP
Push EBX
Push ESI
Push EDI

Вот так по-моему правильней:

Push EBP
Mov EBP,ESP
add esp, (- 20) ; под дворды - 5 шт
add esp, (- 12) ; под ворды - 6 шт
add esp, (-256) ;место под какой-нить буфер

и когда всё выделил, то esp указывает на байт, который сразу за концом буфера. Ниже переменных нет, теперь можно пушить. И только после этого вставляешь свои
Push EBX
Push ESI
Push EDI

если предварительно не выделить под локальные места, то при трёх пушах ты перетрёшь 12 байт, первые три локальных дворда .Угу?

Ответить

Номер ответа: 22
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #22 Добавлено: 23.09.04 08:11
И ещё: я загрузил свою програмку в дебагер, исходник есть, коды - в дебагере и во всех процедурах, где есть локальные, первые три инструкции - это push ebp, mov ebp,esp и add esp, (-localsSize). - в обязательном порядке. А в твоём коде эта последовательность была нарушена.

Ответить

Номер ответа: 23
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #23
Добавлено: 23.09.04 11:14
если предварительно не выделить под локальные места, то при трёх пушах ты перетрёшь 12 байт, первые три локальных дворда

 Я их сохранение вообще уберу. Код локальной процедуры я подсмотрел из хелпа к PB. Зачем он это сохраняет не знаю. Наверное это связано с устройством PB.

Ответить

Номер ответа: 24
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #24
Добавлено: 23.09.04 11:15
SUB MySub(Params)
  ! PUSH EBX ' Automatically added by PowerBASIC
  ! PUSH ESI ' --"---
  ! PUSH EDI ' --"---

  ' the actual SUB code is placed here

  ! POP EDI ' Automatically added by PowerBASIC
  ! POP ESI ' --"---
  ! POP EBX ' --"---
END SUB

Ответить

Номер ответа: 25
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #25
Добавлено: 23.09.04 12:03
по моему, я понял, в чем проблема, ты не выдели место под локальные и сразу начал пушить регистры:

 Опять промазал. Это то тут причём? Я же тебе написал что это "выделение" памяти аналогично инструкции PUSH. Не надо искать там никакую связь. Её там просто нет.

Я первый раз ошибся. Но уточнить не успел - согнали с компьютера :)
Вот так вернее будет

PUSH EBX ; ESP=[EBP-4]
PUSH ESI ; ESP=[EBP-8]
PUSH EDI ; ESP=[EBP-12]

PUSH 0 ; MB_OK | ESP=[EBP-16]
...
CALL [EBP-16]

Ответить

Номер ответа: 26
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #26 Добавлено: 23.09.04 18:30
CyRax, ты меня не путай!! Щас будем ругаться!

Никуда я не промазал. Так, как ты сделал, не делается:

Ты понял к чему я клоню?
 SUB ESP,8
это тоже самое что и
 PUSH r/m/imm32
 PUSH r/m/imm32


Это совсем не то же самое. Продолжаем развивать: см. ниже:

Код локальной процедуры я подсмотрел из хелпа к PB. Зачем он это сохраняет не знаю


Вот зачем: Это не связано с устройством РВ. Есть такой закон: любая процедура (твоя или апишная) обязана сохранить в неизменном виде регистры ebx, esi, edi. Если esi или edi изменятся - твоей проге кердык. Накроется. Потому что покорёжит указатели на данные (сегменты). А не потому, что ты сделал Call 0. (и поэтому тоже выбьет)
eax, ecx и edx могут перетираться.

Именно для этого вставляются эти три пуша, а не для того, чтобы таким "оригинальным" способом выделить место в стеке под локальные.

Дальше: твой код с масмфорума:

It is correct?

CallWindowProc MyProc,0,0,0,0

;MyProc proc p1 :DWORD, p2 :DWORD, p3 :DWORD, p4 :DWORD
Push EBP
Mov EBP,ESP
Push EBX ; ESP=[EBP-4]
Push ESI ; ESP=[EBP-8]
Push EDI

...
SUB ESP,8
MOV [EBP-4],1 ; First Local Variable
MOV [EBP-8],2 ; Second Local Variable
...

Pop EDI
Pop ESI
Pop EBX
Mov ESP,EBP
Pop EBP
Ret 10
;MyProc endp


допустим на входе в процедуру в ebx было 111, в esi было 222.
MOV [EBP-4],1 ;почему EBP-4 ?!!
MOV [EBP-8],2 ;почему EBP-8 ?!!

последними двумя инструкциями ты перетираешь ячейки стека, где сохранены значения ebx и esi. И когда ты будешь возвращаться, откуда ты восстановишь ebx и esi ? В той ячейке, где сохранено 111 ты вписал 1, а там где было 222, вписал 2. И в esi 222 ты уже никогда не получишь. Не восстановишь сегментный регистр. И прога вылетит, даже если всё остальное правильно.

Более того, ты даже цифру 2 не получишь в свой esi, т.к. после его сохранения (push) ты сдвинул указатель на вершину: sub esp,8 и делая рор, загребаешь в эти три регистра совсем с другого места, чем куда положил.

Вот процедура использующая esi, edi

MyProc proc uses esi edi
     local aaaa :dword
     ret
MyProc endp

А вот её код в отладчике: смотри последовательность действий:

PUSH EBP
MOV EBP,ESP
ADD ESP,-4 ;сначала место под локальную
PUSH ESI ;и только потом сохранять регистры
PUSH EDI
POP EDI
POP ESI
LEAVE
RETN
-------------------

Ответить

Номер ответа: 27
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #27
Добавлено: 23.09.04 19:41
Не хочу вступать в полемику. Всё что нужно по этой теме я уже узнал и уточнять думаю нет смысла.
Разве что что нибудь новенькое.

На всякий случай вот тебе код PB-шной процедуры:
:004010E9 55 push ebp
:004010EA 8BEC mov ebp, esp
:004010EC 53 push ebx
:004010ED 56 push esi
:004010EE 57 push edi
:004010EF 83EC60 sub esp, 00000060
...
Это A=5. Как видишь Зейл заранее ограничивает место под переменные и затем отсчитывает от своей границы. 60-4=58
:0040111D B805000000 mov eax, 00000005
:00401122 01C6 add esi, eax
:00401124 8975A8 mov dword ptr [ebp-58], esi
...
:0040115B 8D65F4 lea esp, dword ptr [ebp-0C]
:0040115E 5F pop edi
:0040115F 5E pop esi
:00401160 5B pop ebx
:00401161 5D pop ebp
:00401162 C21000 ret 0010

Ответить

Номер ответа: 28
Автор ответа:
 cresta



Вопросов: 117
Ответов: 1538
 Профиль | | #28 Добавлено: 23.09.04 20:37
Это подход компилятора РВ. Он значительно отличается от того, что компилит masm. И пример с сохранением esi edi ebx - подтверждает. В masm'e если сам не укажешь - регистры сохраняться не будут. Человек сам должен следить за этим. А РВ не полагаясь на человека, сам запихивает их в стек для сохранения. Независимо от того, будешь ты их изменять в процедуре или нет. Ну и результат - лишнее нагромождение кода и путаница.

Ну ладно, разобрались.

Ответить

Номер ответа: 29
Автор ответа:
 CyRax



Разработчик Offline Client

ICQ: 204447456 

Вопросов: 180
Ответов: 4229
 Web-сайт: basicproduction.nm.ru
 Профиль | | #29
Добавлено: 23.09.04 22:15
Эт точно ;)

Ответить

Страница: 1 | 2 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам