¿Cómo llamar al código máquina almacenado en la matriz char?


Estoy tratando de llamar al código nativo del lenguaje máquina. Esto es lo que tengo hasta ahora (obtiene un error de bus):

char prog[] = {'\xc3'}; // x86 ret instruction

int main()
{
    typedef double (*dfunc)();

    dfunc d = (dfunc)(&prog[0]);
    (*d)();
    return 0;
}

Llama correctamente a la función y llega a la instrucción ret. Pero cuando intenta ejecutar la instrucción ret, tiene un error SIGBUS. ¿Es porque estoy ejecutando código en una página que no está habilitada para la ejecución o algo así?

Entonces, ¿qué estoy haciendo mal aquí?

Author: BartoszKP, 2016-10-05

7 answers

Un primer problema podría ser que la ubicación donde se almacenan los datos del prog no es ejecutable.

Al menos en Linux, el binario resultante colocará el contenido de las variables globales en el segmento "data" o aquí, que no es ejecutable en la mayoría de los casos normales.

El segundo problema podría ser que el código que está invocando no es válido de alguna manera. Hay un cierto procedimiento para llamar a un método en C, llamado llamando a la convención (usted podría estar usando el "cdecl", por ejemplo). Puede que no sea suficiente que la función llamada solo "ret". También podría tener que hacer un poco de limpieza de pila, etc. de lo contrario, el programa se comportará inesperadamente. Esto podría ser un problema una vez pasado el primer problema.

 51
Author: Horia Coman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:10

Necesita llamar a memprotect para que la página donde vive prog sea ejecutable. El siguiente código hace esta llamada, y puede ejecutar el texto en prog.

#include <unistd.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>

char prog[] = {
   0x55,             // push   %rbp
   0x48, 0x89, 0xe5, // mov    %rsp,%rbp
   0xf2, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00,
       //movsd  0x0(%rip),%xmm0        # c <x+0xc>
   0x00,
   0x5d,             // pop    %rbp
   0xc3,             // retq
};

int main()
{
    long pagesize = sysconf(_SC_PAGE_SIZE);
    long page_no = (long)prog/pagesize;
    int res = mprotect((void*)(page_no*pagesize), (long)page_no+sizeof(prog), PROT_EXEC|PROT_READ|PROT_WRITE);
    if(res)
    {
        fprintf(stderr, "mprotect error:%d\n", res);
        return 1;
    }
    typedef double (*dfunc)(void);

    dfunc d = (dfunc)(&prog[0]);
    double x = (*d)();
    printf("x=%f\n", x);
    fflush(stdout);
    return 0;
}
 48
Author: Rudi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-06 12:26:20

Como todo el mundo ya ha dicho, debe asegurarse de que prog[] es ejecutable, sin embargo, la forma correcta de hacerlo, a menos que esté escribiendo un compilador JIT, es poner el símbolo en un área ejecutable, ya sea utilizando un script enlazador o especificando la sección en el código C si el compilador lo permite, por ejemplo:

const char prog[] __attribute__((section(".text"))) = {...}
 30
Author: Ismael Luceno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-20 00:12:24

Prácticamente todos los compiladores de C le permitirán hacer esto incrustando lenguaje ensamblador regular en su código. Por supuesto que es una extensión no estándar de C, pero los escritores del compilador reconocen que a menudo es necesario. Como una extensión no estándar, tendrá que leer el manual del compilador y comprobar cómo hacerlo, pero la extensión GCC "asm" es un enfoque bastante estándar.

 void DoCheck(uint32_t dwSomeValue)
 {
    uint32_t dwRes;

    // Assumes dwSomeValue is not zero.
    asm ("bsfl %1,%0"
      : "=r" (dwRes)
      : "r" (dwSomeValue)
      : "cc");

    assert(dwRes > 3);
 }

Dado que es fácil desechar la pila en ensamblador, los compiladores a menudo también le permiten identificar registros lo usarás como parte de tu ensamblador. El compilador puede entonces asegurar que el resto de esa función se aleja de esos registros.

Si estás escribiendo el código del ensamblador tú mismo, no hay una buena razón para configurar ese ensamblador como una matriz de bytes. No es solo un olor a código, diría que es un error genuino que solo podría ocurrir al no saber la extensión "asm", que es la forma correcta de incrustar ensamblador en su C.

 28
Author: Graham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-05 17:17:28

Esencialmente esto ha sido reprimido porque era una invitación abierta a los escritores de virus. Pero puede asignar y buffer y configurarlo con código de máquina nativo en C recto - eso no es problema. El problema es llamarlo. Si bien puede intentar configurar un puntero de función con la dirección del búfer y llamarlo, es muy poco probable que funcione, y muy probable que se rompa en la próxima versión del compilador si de alguna manera logra persuadirlo para que haga lo que desea. Así que el mejor la apuesta es simplemente recurrir a un poco de ensamblaje en línea, para configurar el retorno y saltar al código generado automáticamente. Pero si el sistema protege contra esto, tendrá que encontrar métodos para eludir la protección, como Rudi describió en su respuesta (pero muy específico para un sistema en particular).

 9
Author: Malcolm McLean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-05 10:42:18

Un error obvio es que \xc3 no está devolviendo el double que usted afirma que está devolviendo.

 5
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-05 07:11:34

Puede eliminar el bloqueo permitiendo que el compilador almacene la matriz en la sección de solo lectura de su memoria de proceso (si se conoce en tiempo de compilación). Por ejemplo, declarando el array const.

Ejemplo:

const char prog[] = {'\xc3'}; // x86 ret instruction

int main()
{
    typedef double (*dfunc)();

    dfunc d = (dfunc)(&prog[0]);
    (*d)();
    return 0;
}

Alternativamente puede compilar el código con disabled stack protection gcc -z execstack.

Pregunta relacionada:

 4
Author: moooeeeep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:46:58