wow, esto resultó ser mucho más doloroso de lo que esperaba. El 100% del dolor fue Linux, protegiendo al programa de la sobreescritura y/o la ejecución de datos.
Dos soluciones se muestran a continuación. Y una gran cantidad de google se involucró así que el algo simple de poner algunos bytes de instrucciones y ejecutarlos fue mío, el mprotect y la alineación en el tamaño de la página fue seleccionado de las búsquedas de Google, cosas que tuve que aprender para este ejemplo.
El código de auto modificación es sencillo, si toma el programa o al menos solo las dos funciones simples, compile y luego desarme obtendrá los códigos de operación para esas instrucciones. o use nasm para compilar bloques de ensamblador, etc. De esto determiné el código de operación para cargar un inmediato en eax y luego regresar.
Lo ideal es poner esos bytes en algún ariete y ejecutar ese ariete. Para hacer que Linux haga eso, debe cambiar la protección, lo que significa que debe enviar un puntero que esté alineado en una página de mmap. Así que asigne más de lo que necesita, busque la dirección alineada dentro de esa asignación que está en un límite de página y proteja desde esa dirección y use esa memoria para poner sus códigos de operación y luego ejecutarlos.
el segundo ejemplo toma una función existente compilada en el programa, de nuevo debido a que el mecanismo de protección no puede simplemente señalarlo y cambiar los bytes, debe desprotegerlo de las escrituras. Por lo tanto, debe hacer una copia de seguridad de la llamada de límite de página anterior mprotect con esa dirección y suficientes bytes para cubrir el código que se va a modificar. Luego puede cambiar los bytes/códigos de operación para esa función de la forma que desee (siempre y cuando no se desborde en cualquier función que desee seguir usando) y ejecutarla. En este caso, puede ver que fun()
funciona, luego lo cambio para simplemente devolver un valor, llamarlo nuevamente y ahora se ha modificado.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
unsigned char *testfun;
unsigned int fun (unsigned int a)
{
return(a+13);
}
unsigned int fun2 (void)
{
return(13);
}
int main (void)
{
unsigned int ra;
unsigned int pagesize;
unsigned char *ptr;
unsigned int offset;
pagesize=getpagesize();
testfun=malloc(1023+pagesize+1);
if(testfun==NULL) return(1);
//need to align the address on a page boundary
printf("%p\n",testfun);
testfun = (unsigned char *)(((long)testfun + pagesize-1) & ~(pagesize-1));
printf("%p\n",testfun);
if(mprotect(testfun, 1024, PROT_READ|PROT_EXEC|PROT_WRITE))
{
printf("mprotect failed\n");
return(1);
}
//400687: b8 0d 00 00 00 mov $0xd,%eax
//40068d: c3 retq
testfun[ 0]=0xb8;
testfun[ 1]=0x0d;
testfun[ 2]=0x00;
testfun[ 3]=0x00;
testfun[ 4]=0x00;
testfun[ 5]=0xc3;
ra=((unsigned int (*)())testfun)();
printf("0x%02X\n",ra);
testfun[ 0]=0xb8;
testfun[ 1]=0x20;
testfun[ 2]=0x00;
testfun[ 3]=0x00;
testfun[ 4]=0x00;
testfun[ 5]=0xc3;
ra=((unsigned int (*)())testfun)();
printf("0x%02X\n",ra);
printf("%p\n",fun);
offset=(unsigned int)(((long)fun)&(pagesize-1));
ptr=(unsigned char *)((long)fun&(~(pagesize-1)));
printf("%p 0x%X\n",ptr,offset);
if(mprotect(ptr, pagesize, PROT_READ|PROT_EXEC|PROT_WRITE))
{
printf("mprotect failed\n");
return(1);
}
//for(ra=0;ra<20;ra++) printf("0x%02X,",ptr[offset+ra]); printf("\n");
ra=4;
ra=fun(ra);
printf("0x%02X\n",ra);
ptr[offset+0]=0xb8;
ptr[offset+1]=0x22;
ptr[offset+2]=0x00;
ptr[offset+3]=0x00;
ptr[offset+4]=0x00;
ptr[offset+5]=0xc3;
ra=4;
ra=fun(ra);
printf("0x%02X\n",ra);
return(0);
}
http://linux.die.net/man/2/mprotect debe explicar cuáles son los argumentos para mprotect. La función ID para llamar se pasa en EAX y los siguientes argumentos se pasan en EBX ECX y EDX. – KitsuneYMG