c++ - que - matriz de caracteres en c
¿Cómo llamar al código de máquina almacenado en la matriz de caracteres? (7)
Como todos ya dijeron, debe asegurarse de que
prog[]
sea ejecutable, sin embargo, la forma correcta de hacerlo, a menos que esté escribiendo un compilador JIT, es colocar el símbolo en un área ejecutable, ya sea mediante el uso de un script vinculador o especificando la sección en el código C si el compilador lo permite, por ejemplo:
const char prog[] __attribute__((section(".text"))) = {...}
Estoy tratando de llamar al código nativo de lenguaje de máquina. Esto es lo que tengo hasta ahora (se produce 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á autorizada para la ejecución o algo así?
Entonces, ¿qué estoy haciendo mal aquí?
Debe 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;
}
Esencialmente, esto se ha restringido porque era una invitación abierta a los escritores de virus. Pero puede asignar y almacenar en búfer y configurarlo con código de máquina nativo en C directo, eso no es problema. El problema lo está llamando. 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 es muy probable que se rompa en la próxima versión del compilador si de alguna manera logra convencerlo para que haga lo que quiere . Por lo tanto, la mejor opción es simplemente recurrir a un poco de ensamblaje en línea, configurar la devolución 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).
Prácticamente todos los compiladores de C te permitirán hacer esto incorporando lenguaje ensamblador regular en tu código. Por supuesto, es una extensión no estándar de C, pero los escritores de compiladores reconocen que a menudo es necesario. Como extensión no estándar, tendrá que leer el manual del compilador y verificar cómo hacerlo, pero la extensión "asm" de GCC 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 el ensamblador, los compiladores a menudo también le permiten identificar los registros que usará como parte de su ensamblador. El compilador puede garantizar que el resto de esa función se mantenga alejado de esos registros.
Si está escribiendo el código del ensamblador usted 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 suceder si desconocemos la extensión "asm", que es la forma correcta de incrustar el ensamblador en su C.
Puede eliminar el bloqueo permitiendo que el compilador almacene la matriz en la sección de solo lectura de la memoria de su proceso (si se conoce en el momento de la compilación).
Por ejemplo, declarando la matriz
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 la protección de pila deshabilitada
gcc -z execstack
.
Pregunta relacionada:
- ¿Falla de segmentación cuando el byte codifica una función?
Un error obvio es que
/xc3
no devuelve el
double
que usted afirma que está devolviendo.
Un primer problema podría ser que la ubicación donde se almacenan los datos del programa no es ejecutable.
Al menos en Linux, el binario resultante colocará el contenido de las variables globales en el segmento de "datos" o here , 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 convención de llamada (por ejemplo, puede estar usando el "cdecl"). Puede que no sea suficiente para la función llamada simplemente "ret". Es posible que también deba realizar una limpieza de la pila, etc., de lo contrario, el programa se comportará inesperadamente. Esto podría ser un problema una vez que supere el primer problema.