Duda con la pila (stack)

Iniciado por exploiterstack, 28 Mayo 2015, 09:33 AM

0 Miembros y 1 Visitante están viendo este tema.

exploiterstack

Hola muy buenas a todos los del subforo de ensamblador  ;)

Antes de nada quiero decir que después de un tiempo agobiado con el trabajo por fin tengo tiempo para aprender este lenguaje que siempre quise aprenderlo a fondo.

Mi duda es la siguiente, leyendo sobre el manejo de parámetros, las variables locales, y dirección de retorno en el stack me surge la siguiente duda.

Para referenciar las variables locales de una función por que se utilizar el registro EBP en vez de ESP?

Es decir supongamos que una función requiere de 4 parámetros, en este caso se pusearian los cuatro valores a la pila

push valor1
push valor2
push valor3
push valor4

call nombreFuncion

nombreFuncion PROC

push  ebp
   mov   ebp, esp
   sub   esp, X

    mov eax, [ebp+4]         ;Obtengo el primer parámetro
    mov ebx, [ebp+8]         ;Obtengo el segundo parámetro
    mov ecx, [ebp+12]       ;Obtengo el tercer parámetro
    mov edx, [ebp+16]       ;Obtengo el cuarto parámetro

   mov esp, ebp
   pop ebp
   ret

nombreFuncion ENDP

1) Para obtener los parámetros dentro de la función sería de la siguiente manera?
2) Por que hay ocasiones en los que se suele utilizar ESP como desplazamiento.
3) Por que hay otras ocasiones en la que se le pone el signo negativo? es decir eax, [ebp-4] ?

Si alguien puede ayudarme en como seguir la pila en el momento de una ejecución de una función se lo agradecería, no me refiero como se ejecuta si no el estado de los parámetros, variables locales , ret address en sus respectivas posiciones en la pila siguiendo los registros ESP y EBP.
Espero que se haya entendido, un abrazo de antemano!

cpu2

#1
Hola

CitarPara referenciar las variables locales de una función por que se utilizar el registro EBP en vez de ESP?

Seguramente que vengas de algun lenguaje de alto nivel, en realidad no hace falta usar ebp para hacer referencia a variable locales etc... Podrias usar incluso eax.

Se utiliza ebp por defecto ya que esa es su funcion, para tener la base por asi decirlo una referencia, piensa que cada vez que vayas colocando valores en la pila esta se restara en 4 bytes en tu caso.


Sobre las tres preguntas de abajo, pues todo depende de como estes administrando la pila y demas. Sobre el code pues no lo veo la verdad.

Para empezar que valor tiene la X?

Cargas en la pila los parametros, substraes 16 bytes en la pila con los push, luego el call son otros 4 bytes ya que cargas el offset que viene despues del call, para continuar la ejecucion del code despues de la llamada, serian 20 bytes.

Si te fijas cuando carges los valores de ebp en los registros de uso general, no seran los parametros correctos. El code lo veo mal planteado.

Cuando estas en la funcion cargas de nuevo en valor de ebp, y copias el valor de esp actual a ebp, la X la dejo.

La pila estara con 24 bytes para abajo, en realidad en el primer mov a eax, estas copiando el ret offset.

Un saludo.

P.D: Desde mi punto de vista veo una tonteria, que un programador de ASM, tenga que hacer el tipico esquema de pila como si estuviera en C, no quiero ofender a nadie pero lo veo de esa forma.

Ni que estuvieramos en C.

exploiterstack

#2
Hola cpu2, que tal?

Si que vengo de lenguajes de alto nivel, en especial en java...


La "X" seria el numero de bytes a substraer(en este caso serian 16), es cierto que no tiene lógica poner esta instrucción cuando no se esta haciendo uso de variables locales a la función.

Por lo que me mencionas voy entendiendo un poco mas su uso, gracias. Pero respecto a lo de + o - llego a la conclusión que:


  • Cuando se utiliza el - es para acceder a las variables locales.
  • Cuando se utiliza el + es para acceder a los parámetros de la función.

Otra pregunta sobre lo del offset te refieres al return address? que le sumaria otros 4 bytes haciendo un total de 20? pero de todos modos al no ser variables locales no creo que esa linea tenga importancia.

Cuando dices "Se utiliza ebp por defecto ya que esa es su función" te refieres que ese es el papel de ebp o que es la función suya(es decir su contexto)?

Por que se suele decir que nunca hay que hacer uso de desplazamientos de esp por que es difícil asegurarnos que sean los datos correctos?

Por ultimo un contexto de una función es decir el stack frame de una función puede tener acceso al stack frame de otra función?

Un saludo!

cpu2

#3
Cita de: exploiterstack en 28 Mayo 2015, 14:26 PM

  • Cuando se utiliza el - es para acceder a las variables locales.
  • Cuando se utiliza el + es para acceder a los parámetros de la función.

Y que pasa con las globales? Haz un dissasembler de un simple programa en C, y te ayudara, y veras que lo que haces en el code no tiene sentido, igualmente no entiendo como un programador en ASM siga el mismo esquema que en C para administrar la pila.

CitarOtra pregunta sobre lo del offset te refieres al return address? que le sumaria otros 4 bytes haciendo un total de 20? pero de todos modos al no ser variables locales no creo que esa linea tenga importancia.

Si, lee el code que hiciste y veras que pasa.

CitarCuando dices "Se utiliza ebp por defecto ya que esa es su función" te refieres que ese es el papel de ebp o que es la función suya(es decir su contexto)?

Exacto, cada registro tiene su funcion, hay algunos que estan ligado con instrucciones y demas, los mas tipicos son de uso general, eax, ebx, ecx etc... puden tener la misma funcion que ebp, ojo sin meter por medio segmentos, pero no quiero liarte.

CitarPor que se suele decir que nunca hay que hacer uso de desplazamientos de esp por que es difícil asegurarnos que sean los datos correctos?

Si tu mismo estas administrando la pila manualmente y bien, no se porque tiene que ocurrir algun problema. Si quieres usar el estilo C al menos usa enter y para restaurar la pila leave.

CitarPor ultimo un contexto de una función es decir el stack frame de una función puede tener acceso al stack frame de otra función?

Si, porque no podria tenerlo.

Un saludo.

exploiterstack

#4
Hola otra vez cpu2,

Mira en realidad ese code lo he hecho de cabeza nunca he realizado un programa en asm, es mas sigo un libro y primero quiero asentar los conocimientos(se que es mejor ir probando con forme se va avanzando en el lenguaje para que quede mas claro)

Lo que realmente me importa es asentar conocimiento respecto al uso de la pila, ya que todos dicen lo mismo pero creo que se necesita un empujón para llegar a entender su verdadero uso etc...

Lo que realmente me importaba era el por que de estas lineas:


mov eax, [ebp+4]         ;Obtengo el primer parámetro
mov ebx, [ebp+8]         ;Obtengo el segundo parámetro
mov ecx, [ebp+12]       ;Obtengo el tercer parámetro
mov edx, [ebp+16]       ;Obtengo el cuarto parámetro


El de por que se suele utilizar el signo + y el signo - lo cual he llegado a la conclusion que te puse arriba:

+ para acceder a los parámetros de la función.
-  para acceder a las variables locales.

Quiero pensar que todo esto viene precedido de cuando se iguala ebp a esp(mov ebp, esp) por que no se porque se crear un stack frame y se hace que ebp apunte a la cima, para asi ir aumentando y decrementando ebp.

Independientemente de si los desplazamientos eran los correctos o no.

pd: También quería aclarar que ese code que puse no lo hice con la intención de que fuera a funcionar ni mucho menos. Digamos mejor que ha sido una mala pregunta formulada.


Un saludo! ;)

cpu2

Esto seria lo normal:

Código (asm) [Seleccionar]
push ebp               ;salvo el valor actual de ebp
mov ebp, esp           ; copio el la direccion en la que esta apuntando esp a ebp
sub esp, 16          ;bien ahora reservo 16 bytes en la pila


Cuando reservas los 16 bytes es como si estubieras haciendo los 4 push de tu code. Pero sin copiar los valores.

Bien ebp se quedara apuntando al final de la pila por asi decirlo, y esp al principio, abiendo entre la base ebp y el principio esp un espacio de 16 bytes.

Vale porque se usa ahora el -, bien porque ebp esta al principio como dije, y esp se a ido sustrayendo.

Código (asm) [Seleccionar]
push ebp             
mov ebp, esp           
sub esp, 16

mov [ebp-4], var1        ;
mov [ebp-8], var2        ; Pasamos las variables
mov [ebp-12], var3      ;
mov [ebp-16], var4      ;

mov esp, ebp     ; dejamos esp apuntando donde estaba antes, al principio
pop ebp             ; con ebp igual lo dejamos con su valor anterior
ret                  ; volveriamos al offset despues del call que llamo a esta funcion.


Bien este es el esquema normal, pero si te fijas, en tu code antes de llamar a la funcion pusiste los parametros antes, eso significa que no usaste ebp si no esp.

Vuelvo a repetir, que despues de hacer el call, dejaste unos 20 bytes sustraidos sumando los 4 parametros y el offset para ret.

Bien despues de hacer la llamada salvas de nuevo ebp, y copias el offset actual de esp a este. Y luego reservas X bytes para variables o lo que sea, si quieres tomar los valores de los push tienes que hacerlo con el sumar logico.

Código (asm) [Seleccionar]
mov eax, [ebp+20]                       ;1 valor
mov ebx, [ebp+16]                     ; 2 valor
mov ecx, [ebp+12]                      ; 3 valor
mov edx, [ebp+8]                       ; 4 valor


En el +4 estaria el offset de ret, y sin nada pues el valor de ebp. Y ahora podrias seguir el mismo metodo de arriba para variables o lo que sea.

No se si esto responde tu pregunta.

Un saludo.