Страница: 1 | 2 | 
		
		 
			   
			 
			 
			 
			 
			
 
  
		
     
  
    
Вопрос: Почему обнуляется?
     
    
Добавлено: 22.09.04 03:33
     
      
  
				
			  
					 
			
				 
    
		
       
    
Автор вопроса:  
     CyRax
 CyRax  | Web-сайт: basicproduction.nm.ru | ICQ: 204447456
 | Web-сайт: basicproduction.nm.ru | ICQ: 204447456 
      
       
  
 
    
				
		
		
					 
			
				 
  
		
     
  
    
Ответы
     
    
Всего ответов: 29
     
      
  
		
	  
			 
	
		 
    
       
    
Номер ответа: 16 
      
Автор ответа: CyRax
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #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
 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. Поэтому от манипуляций со стеком адреса не теряются.
) переменные мы адресуем по первоначальному 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
Опять же это всё имхо. Хотя вроде код рассмотренный в отладчике, подтверждает это.
Тут я не понял сути вопроса  
		
	  
			 
	
		 
    
       
    
Номер ответа: 18 
      
Автор ответа: CyRax
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #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
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #19
      
Добавлено:  23.09.04 06:19
       
    
       
  
 
    
 Ты не прав. Нормально он всё записывал. Только после стековых команд оно обнулялось. Может быть даже я сам его затирал. Не веришь? Смотри:
 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
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #20
      
Добавлено:  23.09.04 06:40
       
    
       
  
Ты понял к чему я клоню?
 
    
 SUB ESP,8
это тоже самое что и
 PUSH r/m/imm32
 PUSH r/m/imm32
только предыдущие их значения не затираются новыми.
Ты можешь первое заменить на второе и это будет верно. Вобщем устройство стека я думаю мы с тобой поняли  
		
	  
			 
	
		 
    
       
    
Номер ответа: 21 
      
Автор ответа: cresta
 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
 cresta


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




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #23
      
Добавлено:  23.09.04 11:14
       
    
       
  
 
    
 Я их сохранение вообще уберу. Код локальной процедуры я подсмотрел из хелпа к PB. Зачем он это сохраняет не знаю. Наверное это связано с устройством PB.
		
	  
			 
	
		 
    
       
    
Номер ответа: 24 
      
Автор ответа: CyRax
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #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
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #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
 cresta


Вопросов: 117
Ответов: 1538
      
 Профиль |  | #26
       
Добавлено:  23.09.04 18:30
       
    
       
  
CyRax, ты меня не путай!! Щас будем ругаться!
 
    
Никуда я не промазал. Так, как ты сделал, не делается: 
 SUB ESP,8 
это тоже самое что и 
 PUSH r/m/imm32 
 PUSH r/m/imm32
Это совсем не то же самое. Продолжаем развивать: см. ниже:
Вот зачем: Это не связано с устройством РВ. Есть такой закон: любая процедура (твоя или апишная) обязана сохранить в неизменном виде регистры ebx, esi, edi. Если esi или edi изменятся - твоей проге кердык. Накроется. Потому что покорёжит указатели на данные (сегменты). А не потому, что ты сделал Call 0. (и поэтому тоже выбьет)
eax, ecx и edx могут перетираться.
Именно для этого вставляются эти три пуша, а не для того, чтобы таким "оригинальным" способом выделить место в стеке под локальные.
Дальше: твой код с масмфорума:
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
 CyRax 




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #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
 cresta


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




Разработчик Offline Client
ICQ: 204447456 
Вопросов: 180
Ответов: 4229
      
 Web-сайт:  
 Профиль |  | #29
      
Добавлено:  23.09.04 22:15
       
    
       
  
Эт точно  
    