Resultados 1 al 16 de 16

Ensamblador en Windows

  1. #1 Ensamblador en Windows 
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Jaja, me llamareis pesado de tantos temas que he abierto últimamente.

    Resulta que estuve aprendiendo a crear shellcodes en Linux, pero me dió por mirar cómo serían en Windows y parece que es más complicado, por lo menos a mi parecer, no sé, no me hagáis mucho caso, tampoco leí muy a fondo el tutorial.

    El caso es que me gustaría programar algo en ensamblador como un Hola mundo simple, pero tengo dos dudas:

    -Si tuviera el código de dicha aplicación en formato hexadecimal, gracias a un editor hexadecimal, o incluso con Ollydbg, ¿podría inyectar manualmente dicho código y ver si muestra el mensaje en pantalla?
    -La segunda duda es cómo puedo programar en ensamblador en Windows. Leí acerca del tema, y hacía falta MASM y LINK, pero no los encuentro, y me remiten al DDK de Windows, el cual es un archivo bastante extenso. Hace tiempo usé MASM.EXE y LINK.EXE y funcionaba bien, siguiendo la estructura de un código en ASM, pero ahora no los encuentro. ¿Sabéis donde puedo encontrarlo? En caso de que no sea necesario, ¿qué otra alternativa hay?

    Gracias de nuevo, un saludo!
    Citar  
     

  2. #2  
    Moderador Global Avatar de hystd
    Fecha de ingreso
    Jul 2005
    Ubicación
    1, 11, 21, 1211...
    Mensajes
    1.596
    Descargas
    58
    Uploads
    0
    A ver, por partes...

    Primero, la programación en ensamblador a la que haces referencia corresponde únicamente a ensamblador de procesadores con arquitectura x86 (utilizan el juego de instrucciones IA32 o IA64 de Intel). Lo que significa que si ejecutas ese programa con un procesador con arquitectura distinta no lo reconocerá (Por ejemplo un Motorola o un PowerPC).

    Lo digo más que nada porque cuando se dice "en la calle" de "programar en ensamblador" se tiende a pensar en ensamblador para PC o ensamblador para x86, existiendo miles de procesadores/microcontroladores con arquitecturas distinas (y por tanto juego de instrucciones distintos) y por tanto programas distintos y evidentemente incompatibles unos con los otros.

    No obstante, puesto que tu duda se basa en "ensamblador para x86" me centraré en responder básandonos en arquitectura IA32, pero sólo hacerte saber que la programación en esamblador dicho así en su significado, va mucho más allá de eso, y dependerá de qué, para qué y por qué. Es como decir: "Programar en programación orientada a objetos..."

    Bien, partiendo de que en tu caso se trata de programación para IA32, las respuestas a tus preguntas se basan en lo siguiente:

    Si tuviera el código de dicha aplicación en formato hexadecimal, gracias a un editor hexadecimal, o incluso con Ollydbg, ¿podría inyectar manualmente dicho código y ver si muestra el mensaje en pantalla?
    Si. De hecho, las inyecciones de códigos se basan en eso, en modificar una o varias instrucciones de un programa/proceso por otras. La forma de hacerlo es lo que diferencia una técnica de otra. Del mismo modo cuando se dice "en la calle": "inyección de código" o "inyección DLL" se alude a otras técnicas que no tienen que ver en sí mismas con la filosofía de inyección de código: cualquier mecanismo que logra modificar el flujo de ejecución normal de un proceso. Si observas todos los dispositivos, hardware, sistemas, componentes, módulos, piezas software, etc... que intervienen en el proceso de por ejemplo desde que en un PC se pulsa una tecla hasta que se visualiza en pantalla comprenderás lo que digo.

    Centrándonos en la inyección de código en su significado puro, da igual que lo hagas manualmente (a través de un editor) o automáticamente (mediante un proceso externo). Piensa que no se está hablando de nada del otro mundo. Saliendo del mundo "Windows" en el caso de Linux, tienes un comando que reemplaza el contexto de un proceso por código de otro programa, el cual heredará todos los atributos del proceso anterior (Tal es el caso del comando exec, o de las funciones execl, execve, etc.. de C).

    En cualquier caso (manual o automáticamente) tienes que tener presente siempre el tamaño de las instrucciones que se quieren reemplazar por las que van a reemplazar. Este punto ya tiene que ver más concretamente con el juego de instrucciones del procesador y de su arquitectura. Así en la IA32, tienes que por ejemplo la instrucción "PUSH registro" ocupa 1 byte, y que si la reemplazas por, por ejemplo, una instrucción de salto "JMP direccion", que ocupa 5 bytes, tras realizar la inyección tendrás que los siguientes 4 bytes correspondientes a las instrucciones siguientes al PUSH habrán sido machacados, y por tanto si no se actúa correctamente el resultado será un software erróneo que probablemente nunca llegue a ejecutarse, desembocando en una excepción.

    Esto en cuanto a "inyección de código". Si nos vamos a "inyección de datos", existe un poco más de libertad en el sentido de que si tienes una variable de tipo "entero" que ocupa 4 bytes (por ejemplo un integer en Delphi, o un int del C), puedes reemplazar el contenido de la variable por otro valor sin ningún problema (por ejemplo cambiar un "int a=15" por un "int a=20"), o incluso puedes reemplazar ese segmento de datos por cualquier otro dato del mismo tamaño, por ejemplo cambiar un int por un long o un Pointer o vete tu a saber... del mismo modo en el tratamiento de strings (Cadenas), vectores y arrays tienes que obtener el tamaño que ocupa el dato, esto es: tamaño_del_array * tamaño_de_cada_dato_del_array, Obteniendo el resultados en bytes. Esto se traduce en que si en un programa tienes la cadena "hola mundo" puedes reemplazarla por otra cadena del mismo tamaño (10 caracteres). Otro tema a parte, es que las instrucciones que recorren la cadena tengan un bucle en el que se producen 11 iteraciones (por ejemplo mediante un "cmp ecx,10", "jne @siguiente_Caracter").

    En cuanto a tu segunda duda:

    La segunda duda es cómo puedo programar en ensamblador en Windows. Leí acerca del tema, y hacía falta MASM y LINK, pero no los encuentro, y me remiten al DDK de Windows, el cual es un archivo bastante extenso. Hace tiempo usé MASM.EXE y LINK.EXE y funcionaba bien, siguiendo la estructura de un código en ASM, pero ahora no los encuentro. ¿Sabéis donde puedo encontrarlo? En caso de que no sea necesario, ¿qué otra alternativa hay?
    Puesto que tu caso se remonta simplemente a programar en ensamblador bajo arquitectura x86 (siguiendo el juego de instrucciones IA32), entonces puedes emplear cualquier entorno que permita la programación en ensamblador. No tiene por qué ser un entorno específico o un compilador que trate a bajo nivel. Puedes usar por ejemplo C, y utilizar la directiva "__asm" del compilador o también puedes utilizar Delphi haciendo uso de la directiva "asm", y un largo etc...

    El DDK de Microsoft es el Driver Development Kit (Kit de desarrollo de Drivers), y no tiene por qué programarse en ensamblador, de hecho se programa en C y C++, y muy pocas veces en ensamblador. Como dices, es muy extenso en documentación, y lo que incorpora son unas librerías con funciones y estructuras de datos y un compilador específico para poder compilar y enlazar el código para obtener al final el driver (fichero .sys), y no tiene nada que ver ensamblador con diseñar drivers, vamos de hecho utilizando las DDK se programa siguiendo programación orientada a objetos. La única relación entre ambos tal vez sea que el nivel de abstracción que se debe tener es a bajo nivel, es decir, cercano a la máquina. (Pero nada con la forma de programación).



    A modo de resumen, "programar a pelo en ensamblador x86" sólo se utiliza en algunos casos:

    1º Rutinas de optimización (esto englobaría cualquier código que deba ser optimizado por alguna razón).
    2º Acceso eficiente al hardware
    3º Programas de rendimiento y diagnóstico (aplicaciones de benchmark)
    4º Rutinas para gráficos
    5º Rutinas para protección de software.
    6º y pongo un punto 6 por si me quedara algo más en el tintero.


    Para lo demás, está la programación de alto nivel, que para algo se inventó, y luego el compilador que traduzca ese código a ensamblador.

    Y dichas rutinas pueden formar parte de un software de alto nivel, una aplicación, una librería, un módulo, un driver o un firmware. Al fin y al cabo sigue siendo código que hemos escrito y la única diferencia es que le hemos ahorrado trabajo al compilador.

    Un saludo.
    Última edición por hystd; 21-09-2009 a las 02:05
    El optimista tiene ideas, el pesimista... excusas

    Citar  
     

  3. #3  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Gracias hystd, me quedó más claro todo. Pero con programación en ensamblador me refería a las múltiples estructuras en ficheros .asm que veo por ahi en plan

    segment Pila
    db 150
    pila ends
    .
    ..
    ...

    que varía dependiendo del SO. De todas formas, este nuevo post que escribo no es una pregunta, es sólo información. Conseguí unos cuantos compiladores tanto para windows como para DOS (MASM, NASM...) y probé algunos códigos de ejemplo y funcionan. Esto está bien para probar algunas aplicaciones, pero no lo veo muy rentable, es como tú dices, para algo está la programación a alto nivel.

    De todas formas, probaré la función __asm de C, que no me acordaba de ella jeje.

    Y gracias de nuevo hystd!
    Citar  
     

  4. #4  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    De todas formas, ahora sí que me ronda una pregunta. A ver, yo quiero probar un código en ASM en Windows para imprimir un Hola mundo en pantalla. El caso es que del ejemplo que ví, obtuve esto:

    Código:
    Pila    segment stack 'stack'
            db 256 dup (?)        
    Pila    ends
                                      
    
    Datos   segment 'data'
            Saludo db '*Hola DOS!$'
    Datos   ends
    
    Codigo  segment 'code'
            assume CS:Codigo, DS:Datos, SS:Pila
    
    Main:
    
            mov     ax, seg Datos
            mov     ds, ax                   
            mov     dx, offset Saludo
            mov     ah, 9                    
            int     21h                     
    
            mov     ah, 4Ch                
            int     21h
    
    Codigo  ends
    
            end Main
    Y cuando creo el ejecutable de dicho código, si lo depuro con OllyDbg, veo un montón de instrucciones en ASM que no aparecen ahí, que supongo que serán añadidas por el compilador. El caso es que no las encuentro en la depuración. Mi duda es si podría hacer un código directamente con instrucciones en ASM pero sin especificar la estructura de segmentos como en el código que puse. Algo parecido a hacer shellcodes para linux. Es porque quiero probar a inyectar manualmente un Hola mundo en cualquier otro proceso y ver si se muestra por pantalla. Y claro que esto conlleva conseguir alguna herramienta que me proporcione los hexadecimales de los opcode de las instrucciones.

    Un saludo!
    Citar  
     

  5. #5  
    Moderador Global Avatar de hystd
    Fecha de ingreso
    Jul 2005
    Ubicación
    1, 11, 21, 1211...
    Mensajes
    1.596
    Descargas
    58
    Uploads
    0
    Y cuando creo el ejecutable de dicho código, si lo depuro con OllyDbg, veo un montón de instrucciones en ASM que no aparecen ahí, que supongo que serán añadidas por el compilador.
    Tal vez las instrucciones no aparezcan, o más bien no las ves, porque no estás traceando donde corresponde. Piensa que todo proceso es llamado por el SO como una llamada a función (llamada a la función main del proceso). Por tanto deberá empezar con un PUSH EBP; MOV EBP, ESP. Tras lo cual puede ocurrir que el compilador establezca una llamada (un CALL) hacia el código en cuestión. Eso dependerá de cada compilador. Si aún así no consigues ver, tal vez se deba a que el mismo compilador hace una traducción de las instrucciones por el tema de optimización.

    Usando por ejemplo Visual Studio y programando en C con la directiva "__asm" tienes que el código lo conserva tal cual.

    Es porque quiero probar a inyectar manualmente un Hola mundo en cualquier otro proceso y ver si se muestra por pantalla. Y claro que esto conlleva conseguir alguna herramienta que me proporcione los hexadecimales de los opcode de las instrucciones.
    Los opcodes de las instrucciones los puedes obtener a partir del datasheet del procesador. Por ejemplo para los x86 puedes ir a la web de Intel.com y descargartelo. Allí aparecen los distintos formatos para cada una de las instrucciones (Piensa que esa arquitectura se trata de procesadores CISC, por lo que las instrucciones poseen longitudes y formatos distintos).

    Para el tema de inyección, a ver, no hay un método fijo. El método que viste en un manual que encontraste, y que estuvimos viendo en el hilo: http://foro.hackhispano.com/showthread.php?t=34665, es tan sólo un método más de inyección de código.

    Primero hay que distinguir a quien inyectar, es decir, saber si se va a inyectar códigos/datos a un programa o inyectar código/datos a un proceso. Eso tan simple es sumamente importante.

    Para el segundo caso, debes leer y escribir directamente de memoria, puesto que el código a modificar se encuentra ya cargado en algún segmento de ella (que puede ser un segmento compartido o no). Para el primero simplemente lees el código de un programa y lo modificas en disco, de forma que cuando sea cargado en memoria su funcionamiento habrá variado.

    El ejemplo que vimos en el enlace hacia el hilo ese, se trata del primer caso: se modificaba un programa y no un proceso. Más concretamente se modificaba el código de una función exportada en una librería (una DLL del sistema), para que cada vez que fuera cargada en memoria hiciera lo que marcase la modificación correspondiente (saltar hacia otra dirección ejecutando código externo).

    Hay muchas formas de hacer inyección.

    Inyectar un "hola mundo", puedes hacerlo de mil maneras... ¿Quieres inyectarlo al proceso en ejecución o al programa antes de ser ejecutado?

    Si es lo segundo, ya conoces una forma (la que has visto en ese manual).

    Pero para hacerte una idea de lo que digo, piensa en lo siguiente:

    ¿Qué pasaría si accedes a la variable que apunta al principio de la cadena dentro del segmento de datos apuntado por el registro DS del procesador, y cambias la cadena "Hola DOS!" por otra de igual longitud? ¿y por otra de distinta longitud? ¿Qué pasaría si colocas la cadena que quieres inyectar en una zona de memoria, y haces que la variable apunte a esa zona en vez de al principio de "Hola DOS!"? ¿Qué ocurriría si esa cadena no es accesible o no está cargada en memoria en el momento de acceder a ella?
    etc...

    Por el contrario, si es lo primero, prueba a leer de la memoria (usando por ejemplo la función de la API WIN32 ReadProcessMemory, y una vez sabiendo lo que hay que modificar, lo escribes con WriteProcessMemory). Eso si, acuérdate de los permisos de lectura/escritura a las páginas de memoria que deseas modificar (usar la función VirtualProtect de la API)

    Sabiendo bien el sistema operativo, su arquitectura, sus herramientas, el proceso, el procesador, el sistema, el hardware etc... sumado a tu ingenio, tendrás ingeniería inversa.

    Mi recomendación: saber primero hacer y construir, y luego saber modificar.

    Si te vas a poner a modificar este tipo de cosas a bajo nivel, primero te recomiendo saber hacer cosas a ese nivel y estudiar su comportamiento.

    Un saludo.
    El optimista tiene ideas, el pesimista... excusas

    Citar  
     

  6. #6  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Gracias hystd, me pondré a informarme sobre ensamblador y arquitectura.
    Citar  
     

  7. #7  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Por cierto hystd, me puse a probar lo que dijiste de la función __asm. El código es el que sigue:
    Código:
    #include "stdafx.h"
    #include <windows.h>
    #include <Tlhelp32.h>
    #include <stdio.h>
    
    
    int main(int argc, char* argv[])
    {
    __asm mov eax, ebx;
    
    }
    El caso es que luego genero el exe y depuro con OllyDbg, pero no veo esa instruccion por ningún lado. Lo único que veo son muchos CALL en la depuración (GetHandler, createHeap, etc) y no hay manera :S ¿No hay una forma más limpia de depurar sólo el código que escribo? Gracias
    Citar  
     

  8. #8  
    Moderador Global Avatar de hystd
    Fecha de ingreso
    Jul 2005
    Ubicación
    1, 11, 21, 1211...
    Mensajes
    1.596
    Descargas
    58
    Uploads
    0
    Eso ocurre porque los ejecutables (.exe) son ficheros que necesitan unos pasos previos antes de empezar a ejecutar el código de los mismos. Esos pasos incluyen la reserva de espacio de memoria, la asignación de los registros, reubicación de cada uno de los segmentos (datos, códigos, pila) y de las funciones que se utilizan y todo lo demás relacionado con el contexto del proceso, de ahí que todos los .exe's hagan las distintas llamadas al sistema (CreateProcess, GetModuleHandle, GetProcAddress, VirtualProtect, etc...) y una vez hecho todos estos pasos se salta al main, el cual comenzará con un PUSH EBP; MOV EBP, ESP

    Para depurar tal y como quieres hay dos opciones:

    1º Que el ejecutable sea una imagen exacta de memoria. En tal caso estaríamos hablando de un fichero .com. Puedes crearlos a partir de la herramienta debug.exe de Windows.

    2º Siguiendo la línea que llevamos, que es lo recomendable, insertar un breakpoint en la llamada correspondiente a la función que quieres depurar (En este caso el main). Normalmente suele corresponder con la última llamada, no obstante, dependiendo de cada compilador puede que tras el main se realice una o más llamadas más para salir del contexto del proceso (con lo cual el CALL hacia el main ya no sería el último ).

    ¿Cómo averiguar dónde está mi código ensamblador o cualquier otro código en OllyDbg? Pues un método para este tipo de programas simples, es cargar el .exe y empezar a tracear con F8 (tracear con F8 en OllyDbg es hacer un "step over", es decir, ejecutar paso a paso pero sin entrar en el código de las llamadas hechas con un CALL). Cuando llegues al CALL que llama al main de mi programa (lo sabes porque se habrá iniciado el proceso), pones un breakpoint a dicha llamada y vuelve a cargarlo. Vuelve a tracear con F8 y esta vez cuando se llegue al CALL hacia el main, se parará la traza y podrás visualizar el código que quieres.

    Otro método es ir traceando con F7 ("step into" en OllyDbg, que equivale a ejecutar paso a paso, pero entrando en el código de cada CALL) y en algún momento llegarás al main. Este método es un poco "más aburrido" o "más largo" porque te tienes que tragar todas las llamadas previas antes del main para ubicar el contexto del proceso.

    Para ejecutables de mayor tamaño, hay una opción que es visualizar todas las llamadas que se realizan ("Search for" -> "all intermodular calls"), y pones el breakpoint donde quieres depurar.

    Esto, cuando cojas un poco de vicio no te costará mucho encontrar lo que buscas para cualquier programa propio. En otros (comerciales) tal vez cueste un poco más por aquello de la protección (para antidebugging o para evitar el cracking y la tarea se dificulta un poco más), pero normalmente encontrar lo que buscas no suele ser tarea difícil.

    Un saludo.
    El optimista tiene ideas, el pesimista... excusas

    Citar  
     

  9. #9  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Pero para buscar el CALL ese que llama al main de mi código, no sé cómo hacerlo, porque dices que vaya con F8 hasta que el proceso se cargue, y entonces ahí ya tendré dicho CALL, pero hay dos problemillas con esto:
    1-Nada más cargo el programa .exe con el depurador, me salta la ventanita de ms-dos en negro, así que desde el comienzo ya salta el proceso, cosa que se contradice con lo que dices
    2-Nada más se carga el programa, las primeras instrucciones son PUSH EBP, MOV EBP,ESP así que ¿está desde el principio el código? Porque yo no veo las instrucciones que metí en __asm.

    Gracias!
    Citar  
     

  10. #10  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Vale hystd, ya lo encontré, estaba un poco lejos jeje. Gracias!
    Citar  
     

  11. #11  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Me recomendaste programar ASM en C, y yo lo estoy haciendo en Visual C++ 6.0. El problema viene cuando quiero interactuar con datos, por ejemplo, si quiero crear una cadena y mostrarla por pantalla, ¿cómo hago eso?

    Me dijeron en otra parte que todo esto es DOS, que use ASM para Windows, pero me gusta trabajar a bajo nivel para comprender la estructura interna, así que opto por seguir usando las interrupciones del DOS jeje

    Un saludo hystd
    Citar  
     

  12. #12  
    Moderador Global Avatar de hystd
    Fecha de ingreso
    Jul 2005
    Ubicación
    1, 11, 21, 1211...
    Mensajes
    1.596
    Descargas
    58
    Uploads
    0
    Buenas, no te he podido responder antes porque aún estoy fuera...

    Para tu duda, simplemente creas las variables tal cual dentro de tu función: "int numero;", "char letra;", "char* cadena;", "long pepito;", etc... y dentro del código asm, cuando necesites acceder a una de ellas simplemente debes obtener la dirección de la variable y luego utilizando uno de los modos de direccionamiento soportados por el procesador, lees o escribes en esa variable.

    A modo de ejemplo: "sumar dos números en ensamblador":

    Código:
    #include <stdio.h>
    
    void main (void){
       int a, b, resultado;
    
       printf("Introduce numero a: \n");
       scanf("%d", &a);
       printf("Introduce numero b: ", &b);
       scanf("%d", &b);
    
       __asm{
            lea esi, a                    //esi=dirección de variable a
            lea edi, b                    //edi=dirección de variable b
            mov eax, [esi]            //eax=contenido de variable a
            mov ebx, [edi]            //ebx=contenido de variable b
            add eax, ebx              //eax=eax+ebx
            lea edx, resultado       //edx=dirección de variable resultado
            mov [edx], eax           //resultado=eax=suma de a + b
       }
    
       printf("Resulado: %d\n", resultado);
    }
    Por supuesto hay múltiples formas de hacerlo... y mucho más si el procesador es de arquitectura CISC.


    En cuanto a un ejemplo de imprimir por pantalla una cadena de texto, pues debes tener en cuenta un pequeño detalle...

    El programa que logres compilar, cuando sea ejecutado por el sistema operativo, lo hará en modo usuario, por lo que el acceso a los periféricos directamente ("directamente" quiere decir acceder a los registros o mapa de memoria del periférico), desde ese programa desembocarán en una excepción impidiendo por tanto dicho acceso.

    Por tanto, ¿Qué mecanismos hay para acceder en un sistema operativo con protección a los periféricos? La respuesta es mediante llamadas al sistema.

    En el caso de DOS se disponía de interrupciones software (instrucciones INT xx), las cuales identificaban mediante el número "xx", la SSI de ésta desviando el flujo de ejecución hacia la primera instrucción apuntada por el vector de interrupción asociado. En Windows (superiores a NT4.0), se dispone de la API WIN32. (No voy a entrar ahora en más detalles de por qué a los cambios ni lo que ocurre minuciosamente).

    Puesto que en tu caso se trata de una aplicación DOS emulada bajo un Windows con arquitectura NT, puedes usar tanto una como otra. Es decir, puedes usar funciones de la API (echa un vistazo a la MSDN sobre el uso de la consola: http://msdn.microsoft.com/en-us/library/ms682055(VS.85).aspx), o bien, como ya has planteado, usar directamente las funciones del DOS, (mediante la interrupción INT 21, con la función ah=09). De un modo u otro, sería recomendable que echaras un vistazo a todas las funciones del DOS si vas a seguir desarrollando aplicaciones DOS (hay para el manejo del ratón, del teclado, de sonido, de acceso al disco, a ficheros, etc...)... en la época del MS-DOS, conocer todas estas funciones significaba ser "un gurú" de la programación jejeje.

    La cuestión radica en ver cómo realizar las llamadas a esas funciones.

    Todo radica en ver cómo funciona el tema de las llamadas en ensamblador. Como ya sabes, ésto se lleva a cabo mediante la instrucción CALL $dirección_funcion, pero la mayoría de las veces las funciones o procedimientos a los que se llaman reciben parámetros (y en el caso de las funciones, devuelven un resultado).

    ¿Cómo paso los parámetros? La respuesta es: Mediante la pila.

    Existen estándares que implementan las funciones llamadas y que el proceso que llama a estas funciones debe cumplir si se quiere que el código funcione... existen por ejemplo los estándares cdecl, stdcall, syscall, fastcall, etc... (y que no voy a explicar ahora...) y que cada API, cada función, o cada cual adapta según sus conveniencias. En el caso de la API WIN32, el estándar segudio es el stdcall... (sería recomendable que echaras un vistazo a este tema antes de seguir, y si no entiendes algo, pregunta).

    Así, los parámetros se pasan mediante instrucciones PUSH (de derecha a izquierda si es stdcall), y cuando ya estén todos, entonces se llama a la función con un CALL.

    Esta función llamada, siguiendo el estandar stdcall (también ocurre con algunos otros) lo primero que hará será hacer un PUSH EBP; MOV EBP, ESP; que prepara la pila para su uso interno, y para poder acceder a los parámetros... por ejemplo mediante un MOV EAX, [EBP+8]... (no quiero ser pesado, ni extenderme mucho más... pero si quieres puedes leer los números de la ezine de aquí como punto de partida en este tema).



    No obstante, gracias a los fabricantes de compiladores, que escribieron código engorroso por nosotros, implementaron funciones de manejo mucho más fácil que toda la API, de forma que el programador no tuviera que interactuar directamente con ella. Así por ejemplo en C, tienes la función "fopen" para abrir un fichero, definido como tipo FILE*, y que utiliza las llamadas de más bajo nivel "open" y "fdopen" para el manejo de descriptores de fichero, las cuales ya interactúan con el sistema operativo... en el caso de Windows, llamando a "CreateFile".

    En tal caso, si optas por no interactuar con la engorrosa API, puedes hacer en tu código ensamblador un CALL directamente a dicha función (sabiendo que en C, el estandar seguido es cdecl). Para el caso de "imprimir una cadena de texto", puedes hacer uso de la función "printf" que ya implementa C, y según sea el sistema operativo, dicha función llamará a las funciones del sistema que sean necesarias para llevar a cabo la tarea (siendo ya el SO quien accede al hardware).

    Así a modo de ejemplo, para "imprimir una cadena de texto" en ensamblador, usando C, puedes hacer lo siguiente:

    Código:
    #include <stdio.h>
    
    void main(){
        const(char*) cadena = "Hola DOS\n";
        
        __asm{
             push dword ptr cadena
             call printf
             add esp,8
        }
    }

    Después de lo que llevas de lectura de este post, te remito al post: http://foro.hackhispano.com/showthread.php?t=34665#17, que ya hablamos y en donde te explico brevemente el flujo de ejecución de un proceso que accede al hardware, por si te entra la duda de "interactuar" a más bajo nivel.

    Un saludo.
    El optimista tiene ideas, el pesimista... excusas

    Citar  
     

  13. #13  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Vale muchas gracias, me quedó más claro jeje.
    Citar  
     

  14. #14  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    Hystd, te molesto de nuevo por este hilo del foro :P

    Una cosita que nos dejamos en el tintero. Vale, eso que me dijiste de interactuar con variables y usando la funcion __asm y las funciones de C y de Windows incluso, está perfecto. Es una manera de dejar de usar las INT del DOS. Pero me inquieta algo: si yo quiero por ejemplo inyectar en un proceso un código, ¿cómo hago para saber qué código hexadecimal es?

    Puede que no me haya explicado bien. En un libro que tengo, estuve leyendo exhaustivamente el capítulo de Shellcodes, orientado al SO Linux. Es relativamente fácil crear una shellcode haciendo llamadas al sistema, y tan solo ocupa menos de 100 bytes, con lo que puede inyectarse en cualquier proceso fácilmente. Pero si yo quiero hacer un volcado de memoria de algún ejecutable usando __asm, es bastante grande. Sin embargo, en linux, hago un volcado con hexdump, copio los hexadecimales, y podría decirse que ya dispongo de una shellcode. Es más, hacía una prueba para mostrar un "Hola mundo" dentro de otro proceso. Pero si en un proceso de Windows quiero hacre una prueba con un "Hola mundo", ¿cómo calculo los hexadecimales de mis instrucciones ASM? :S Es lo que no me queda claro.

    Hablamos en otro post sobre la inyección de código en procesos en ejecución. Eso me quedó claro, pero para crear un buffer con instrucciones para luego ejecutarlas, necesitaré saber sus opcode y que todas esas instrucciones tengan sentido, no vale poner un JMP, un MOV y un RET sin sentido, sino que tengan coherencia (hacer un código en condiciones que meta en pila un punteor a una cadena, llame a printf y sume a la pila para que no haya problemas con el parámetro de printf, a modo de ejemplo). Como vimos en lo de inyección DLL, metíamos en un buffer la instruccion 0xE9, pero claro, si yo quisiera meter las instrucciones correspondientes a mostrar un Hola mundo, ¿cómo lo hago? Pero no es el cómo sino el qué meto en dicho buffer. Gracias tio!
    Citar  
     

  15. #15  
    Moderador Global Avatar de hystd
    Fecha de ingreso
    Jul 2005
    Ubicación
    1, 11, 21, 1211...
    Mensajes
    1.596
    Descargas
    58
    Uploads
    0
    Una cosita que nos dejamos en el tintero. Vale, eso que me dijiste de interactuar con variables y usando la funcion __asm y las funciones de C y de Windows incluso, está perfecto. Es una manera de dejar de usar las INT del DOS. Pero me inquieta algo: si yo quiero por ejemplo inyectar en un proceso un código, ¿cómo hago para saber qué código hexadecimal es?
    Bueno, no es que dejes de usar las interrupciones o funciones del DOS, lo que haces es que te abstraes a un nivel superior, de forma que durante la implementación de tu programa no tengas que manejar directamente dichas interrupciones o funciones. Probablemente un "printf" bajo Windows, internamente llame a int21 con función ah=09 para representar un caracter por pantalla.

    En cuanto a lo de saber "qué codigo hexadecimal es", no entiendo bien la pregunta... la respuesta a la pregunta es que el opcode de una instrucción viene definido por el procesador y su arquitectura, pero mi intución me dice que no es esa la respuesta que buscas...

    Cuando se habla de "inyección de código" hay que saber tres cosas:

    1º Quien inyecta
    2º A quien se inyecta.
    3º Qué se inyecta

    Respondiendo a ésto, el cómo sale sólo... No es lo mismo inyectar un código o una instrucción (de por ejemplo un salto), directamente a un proceso, que inyectárselo a un módulo o un elmento exportado del módulo que utiliza un proceso...

    Como vimos en lo de inyección DLL, metíamos en un buffer la instruccion 0xE9, pero claro, si yo quisiera meter las instrucciones correspondientes a mostrar un Hola mundo, ¿cómo lo hago? Pero no es el cómo sino el qué meto en dicho buffer. Gracias tio!
    0xE9 es una instrucción de salto relativo (JMP offset). En el caso de la inyección lo que se hacía es que se calculaba el offset hacia el comienzo de mi rutina, la cual por cierto puede llamar a "printf" e imprimir un mensaje por pantalla.

    Normalmente las shellcodes en Windows se basan en obtener la dirección de memoria de los módulos que utiliza un proceso. Y en otros casos, dentro de esos módulos, las direcciones de memoria donde se encuentran las funciones que va a utilizar. Una vez conocidas esas direcciones, lo que se hace es suplantar la dirección de memoria base por otra, de forma que cada vez que un proceso llame a ese módulo (el original), estará llamando realmente al módulo falso (el nuestro). En este caso se está hablando de inyección de código a un módulo (o una DLL).

    La obtención de las direcciones de memoria de los módulos utilizados se consiguen utilizando una estructura de datos que utiliza el propio Windows (el Process Environment Block, o PEB), la cual contiene todo los datos de entorno del proceso (lista de módulos cargados, si el proceso está siendo depurado, etc...).

    Dicha estructura de datos se crea cuando se lanza el proceso (y se almacena en espacio de memoria del usuario, por lo que el acceso a ésta no supone problemas), y suele estar apuntada por el registro FS del procesador. Por lo que accediendo a un campo específico de ésta tendrías la dirección de memoria de los módulos cargados. Cambiando la dirección de ese módulo por la dirección de tu módulo, tendrías hecha la inyección.

    Busca por "PEB Hooking", que hay mucha información al respecto.

    Esto mismo lo consigues haciendo las llamadas a la API WIN32: GetModuleHandle o LoadLibrary, y GetProcAddress... pero si te empeñas en hacerlo en ensamblador, por ahí es por donde tienes que empezar.

    Por otro lado, hay otras técnicas que se basan en obtener la dirección de comienzo del ejecutable (indicada por la cabecera PE de éste, el "EntryPoint"), y a partir de ahí realizar la inyección donde corresponda. En este caso es necesario saber las estructuras de datos de los PE.

    Busca por "PE Headers"

    Un saludo.
    El optimista tiene ideas, el pesimista... excusas

    Citar  
     

  16. #16  
    Moderador HH
    Fecha de ingreso
    Mar 2003
    Ubicación
    Galiza
    Mensajes
    3.919
    Descargas
    8
    Uploads
    1
    Primeramente decir que no me he leido todos los post, pero creo que no se respondía a una de las cuestiones.

    En Windows uno de los entornos de desarrollo en ensamblador más populares y si es fácil de conseguir es Turbo Assembler de Borland (inclulle depurador -turbo debuger-, lincador...). Otro (muy fácil de conseguir y open software) es RosASM para win32 y además es un IDE completo, incluyendo linkador, debuger, desensamblador... Otro es A386 que te crea directamente archivos COM...

    Saludos
    He conocido muchos dioses. Quien niegue su existencia está tan ciego como el que confía en ellos con una fe desmesurada. Robert E. Howard
    La suerte ayuda a la mente preparada.
    Citar  
     

Temas similares

  1. ensamblador basico
    Por eduardo40 en el foro GENERAL
    Respuestas: 1
    Último mensaje: 19-12-2012, 00:00
  2. que es ensamblador y C++?¿
    Por GhosT_Mx en el foro PROGRAMACION DESKTOP
    Respuestas: 11
    Último mensaje: 10-10-2006, 06:06
  3. ayuda en ensamblador please
    Por kaitower en el foro GENERAL
    Respuestas: 1
    Último mensaje: 17-04-2005, 00:18
  4. MASM(macro ensamblador)
    Por EnT en el foro APLICACIONES
    Respuestas: 1
    Último mensaje: 03-03-2002, 07:47
  5. Ensamblador
    Por Clase en el foro INGENIERIA INVERSA
    Respuestas: 1
    Último mensaje: 21-02-2002, 14:06

Marcadores

Marcadores